JavaThread类详解核心属性、常用方法与实践

Thread 类详解:核心属性、常用方法与实践

1)Thread 的定位:管理一条线程执行流的对象

Thread 对象用于描述并管理一条线程执行流。线程真正的调度由底层系统完成,但在 Java 层面创建、启动、命名、观测与中断协作都通过 Thread 完成。


2)创建与启动:start() 才是启动新线程

常见构造方式:

java 复制代码
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("worker-1");
Thread t4 = new Thread(new MyRunnable(), "worker-1");

关键点只有一个:

  • start() 才会创建新线程并进入调度
  • 直接调用 run() 不会创建新线程,只是普通方法调用

示例:

java 复制代码
Thread t = new Thread(() -> {
    System.out.println(Thread.currentThread().getName());
}, "worker-1");

t.start();   // ✅ 开新线程
// t.run();  // ❌ 不会开新线程

3)Thread 的核心属性与用法(重点)

常用属性可以理解为三类问题:

  • "是谁":ID、Name
  • "在干嘛":State、Alive
  • "怎么调度与退出":Priority、Daemon、Interrupted

3.1 线程 ID:getId()(唯一、只读)

含义 :每个线程有唯一 ID。
用法

java 复制代码
long id = t.getId();

实践:日志打印线程 ID 有助于定位并发问题(线程名可能重复,ID 更稳定)。


3.2 线程名:getName()(建议强制规范命名)

含义 :线程名是排障时的"路标"。
用法

java 复制代码
String name = t.getName();

实践 :推荐"业务-角色-序号"命名,例如 pay-worker-1coupon-cleaner-0。(构造时直接传 name 最干净。)


3.3 线程状态:getState()(观测用)

含义 :返回 Thread.State,用于观测线程处于哪个阶段。
用法

java 复制代码
Thread.State s = t.getState();

实践 :状态适合诊断,不建议作为业务分支条件(状态瞬时变化,读到即过时)。

直觉判断:

  • 大量 BLOCKED → 锁竞争严重
  • 大量 WAITING/TIMED_WAITING → 在等条件、等线程结束、或 sleep

3.4 优先级:getPriority()(提示而非承诺)

含义 :优先级高的线程"更可能"获得调度机会,但不保证。
用法

java 复制代码
int p = t.getPriority();

实践:不要依赖优先级保证正确性;顺序、互斥、时序应由同步与协作机制保证。


3.5 守护线程属性:isDaemon() / setDaemon(...)(前台/后台线程差异就在这)

这一块非常关键,因为它直接决定 JVM 什么时候退出

3.5.1 前台线程 vs 后台线程(守护线程)
  • 前台线程(用户线程,non-daemon) :默认创建出来的线程就是前台线程。
    只要 JVM 中还存在任何一个前台线程,JVM 就不会退出

  • 后台线程(守护线程,daemon) :通常承担监控、清理、心跳、统计上报等"服务型/陪跑型"任务。
    当 JVM 中只剩守护线程时,JVM 会直接退出,守护线程会被"硬停",不保证把工作做完。

一句话:前台线程负责"续命",守护线程不续命。

3.5.2 setDaemon 的硬规则
  • t.setDaemon(true):设为守护线程
  • t.setDaemon(false):设为前台线程(默认)

两条硬规则必须记牢:

  1. 必须在 start() 之前调用 ,否则会抛 IllegalThreadStateException
  2. 默认会继承创建它的线程的 daemon 属性 (例如 main 是前台线程,main 创建的线程默认也是前台)
3.5.3 示例:守护线程在"只剩它一个"时被 JVM 结束
java 复制代码
public class DaemonDemo1 {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("daemon still running...");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ignored) {}
            }
        }, "daemon-worker");

        t.setDaemon(true);   // ✅ 必须在 start 前设置
        t.start();

        Thread.sleep(800);
        System.out.println("main ends");
        // main 结束后,如果 JVM 里只剩 daemon 线程,JVM 直接退出
    }
}
3.5.4 对照:前台线程会阻止 JVM 退出
java 复制代码
public class DaemonDemo2 {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("user thread running...");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ignored) {}
            }
        }, "user-worker");

        // 默认就是前台线程
        t.start();

        Thread.sleep(800);
        System.out.println("main ends");
        // main 结束后仍有前台线程,所以 JVM 不退出
    }
}
3.5.5 时机错误:start() 后再 setDaemon 会抛异常
java 复制代码
public class DaemonDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {}, "bad-daemon");
        t.start();

        // ❌ 已经 start 了,再设置 daemon 会抛 IllegalThreadStateException
        t.setDaemon(true);
    }
}
3.5.6 工程取舍建议
  • 适合守护线程:监控采样、指标上报、定期清理、心跳探测、非关键日志刷新
  • 不适合守护线程:写盘落库、事务提交、关键数据同步、订单/支付等必须完成的任务
    原因:JVM 退出时守护线程可能被硬停,收尾不可靠

3.6 是否存活:isAlive()(线程是否已结束)

含义 :线程是否处于存活状态(通常可理解为 run 是否结束)。
用法

java 复制代码
boolean alive = t.isAlive();

实践:不推荐用忙等轮询:

java 复制代码
while (t.isAlive()) {} // 不推荐:空转浪费 CPU

等待结束应使用 join()(见下文)。


3.7 中断标记:interrupt() / isInterrupted()(协作式停止)

含义 :中断不是强杀线程,而是发出"停止请求",由线程自行检查并退出。
用法

java 复制代码
t.interrupt();                         // 发出中断请求
boolean flag = t.isInterrupted();      // 查询中断标记(不清除)

两种典型表现:

  1. 线程在 sleep/wait/join 等阻塞点:常见结果是抛出 InterruptedException
  2. 线程在正常执行:不抛异常,仅设置中断标记,需要自行检测

推荐"优雅停止"写法:

java 复制代码
class Worker implements Runnable {
    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                // do work...
            }
        } finally {
            // 清理资源
        }
    }
}

4)常用方法:高频三件套

4.1 获取当前线程:Thread.currentThread()

java 复制代码
Thread cur = Thread.currentThread();
System.out.println(cur.getName());

用于打印"当前执行流是谁",尤其适合日志与调试。

4.2 休眠:Thread.sleep(ms)

java 复制代码
Thread.sleep(1000);

sleep 只能保证实际休眠时间 指定值;不要把它当精确计时器。

4.3 等待线程结束:join()

java 复制代码
t.join();          // 等待 t 执行结束
// t.join(1000);   // 最多等待 1000ms

isAlive() 忙等更正确、更省 CPU,也更符合线程协作语义。


5)线程属性"体检模板"(排障常用)

java 复制代码
Thread t = new Thread(() -> {
    System.out.println("running...");
}, "diag-1");

System.out.println("id=" + t.getId());
System.out.println("name=" + t.getName());
System.out.println("state=" + t.getState());
System.out.println("priority=" + t.getPriority());
System.out.println("daemon=" + t.isDaemon());
System.out.println("alive=" + t.isAlive());
System.out.println("interrupted=" + t.isInterrupted());

t.start();

6)总结

  • 线程必须命名:线程名是最便宜的可观测性
  • 停止线程用中断协作:interrupt + 周期性检查 + finally 清理
  • 等待结束用 join:不要写忙等轮询
  • 状态用于诊断:不要用 getState() 控制业务逻辑
  • 守护线程要谨慎:只用于"可丢弃/可中断"的后台任务,关键任务禁止 daemon

这套 Thread 属性与方法,本质上就是并发程序的"仪表盘"。把仪表读懂,很多问题就能从"玄学"降级成"可推理的工程问题"。

相关推荐
天天摸鱼的java工程师4 分钟前
工作中 Java 程序员如何集成 AI?Spring AI、LangChain4j、JBoltAI 实战对比
java·后端
星辰_mya4 分钟前
RockerMQ之commitlog与consumequeue
java·开发语言
__万波__5 分钟前
二十三种设计模式(二十二)--策略模式
java·设计模式·策略模式
꧁Q༒ོγ꧂5 分钟前
C++ 入门完全指南(六)--指针与动态内存
开发语言·c++
不想上班的小吕6 分钟前
采购申请创建(BAPI_PR_CREATE/BAPI_REQUISITION_CREATE)
java·服务器·数据库
IT=>小脑虎8 分钟前
2026版 Go语言零基础衔接进阶知识点【详解版】
开发语言·后端·golang
ChangYan.9 分钟前
ffi-napi运行失败,报错:No native build was found,解决办法
开发语言
专注VB编程开发20年9 分钟前
压栈顺序是反向(从右往左)的,但正因为是反向压栈,所以第一个参数反而离栈顶(ESP)最近。
java·开发语言·算法
椰汁菠萝9 分钟前
spring boot下使用gdal解析tif文件
java·native·gdal·0
better_liang10 分钟前
每日Java面试场景题知识点之-ELK日志分析
java·elk·微服务·面试题·日志分析·企业级开发