“牛马小程”短暂的一生之Thread生命周期

61 阅读7分钟

让我们用一个有趣的故事来理解Java中Thread的“生命状态”,保证让你听得津津有味还能彻底明白!


第一章:故事前传 - “线程星球”的入职流程

想象一下,有一个叫  “线程星球”  的地方,这个星球上的每一个居民都是一个Thread对象。他们的一生就是为了完成一件叫做run()的任务。

星球的管理局(JVM)为每一个居民都规定了一套详细的“状态”,用来跟踪他们正在做什么。这些状态被明确定义在Thread.State这个枚举类里:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

现在,让我们跟着一位名叫小程(Thread XiaoCheng)  的新居民,来看看他精彩的一生(状态变化)吧!


第二章:小程的一生 - 状态变迁记

状态1:NEW (新生儿) - 纸上蓝图

小程刚刚被new了出来,他有了自己的身份(内存空间),也知道自己未来要做什么任务(run()方法里的代码)。但是,此刻他还只是一张“蓝图”,还没有真正开始执行,没有任何生命体征。

// 小程诞生了!但还只是一张蓝图
Thread xiaoCheng = new Thread(new Runnable() {
    @Override
    public void run() {
        // 小程的人生任务:买奶茶
        System.out.println("我是小程,我要去买奶茶了!");
        buyMilkTea();
    }
});
// 此时 xiaoCheng.getState() 返回 Thread.State.NEW

比喻:就像你刚刚写好了求职简历,但还没投递出去。


状态2:RUNNABLE (可运行) - 摩拳擦掌,准备开干!

当有人调用了小程的start()方法,他就被“激活”了!他告别了NEW状态,进入了RUNNABLE状态。

xiaoCheng.start(); // 小程,开始你的任务!
// 此时 xiaoCheng.getState() 返回 Thread.State.RUNNABLE

这里有个超级重要的误区!
RUNNABLE状态并不意味着小程正在CPU上执行任务。它意味着小程已经准备好了,随时可以执行。星球的管理局(JVM)的“任务调度员”(线程调度器)会管理所有处于RUNNABLE状态的居民。

  • 正在CPU上执行:我们通常说它处于 Running 状态(这是操作系统层面的概念)。
  • 在就绪队列里等待CPU:它仍然处于 RUNNABLE 状态,在等待调度员喊它的名字。

比喻:小程已经来到了奶茶店门口,排在了队伍里。如果正在柜台前点单,那就是Running;如果还在队伍里等着,那就是Ready(但都属于RUNNABLE状态)。能不能点单,全看调度员(店员)叫不叫下一个。

via.placeholder.com/400x200?tex…


状态3/4:WAITING & TIMED_WAITING (等待中) - “我等你消息哦!”

小程在执行任务时,经常会需要等其他线程的通知。这时,他就会主动放弃CPU,进入等待状态。

  • WAITING (无限期等待) :小程不知道要等多久,可能永远等下去。
  • TIMED_WAITING (有时限等待) :小程只等一段明确的时间。

经典场景:

  1. 调用 object.wait() 方法:小程买奶茶发现珍珠还没煮好,他就对老板娘说:“wait()!我在这等着,煮好了notify()我一下”。然后他就主动去旁边休息了(释放CPU),进入WAITING状态。
  2. 调用 Thread.join() 方法:小程需要等他的好朋友小A买完奶茶才能一起走。他调用了小A.join(),于是进入WAITING状态,直到小A线程执行完毕。
  3. 调用 LockSupport.park() 方法:类似wait,也是挂起。
  4. 调用 Thread.sleep(time) :小程累了,主动说:“我睡5000毫秒”。这是他自己决定的,不需要别人叫醒,时间到了调度员会把他重新变为RUNNABLE。这是TIMED_WAITING
  5. 调用 object.wait(timeout) :和wait一样,但设置了最长等待时间,也是TIMED_WAITING
public void run() {
    synchronized (lock) { // 先拿到锁
        try {
            System.out.println("珍珠还没好,我先wait()...");
            lock.wait(); // 进入WAITING状态,同时会释放lock锁
            // 被notify后,会重新尝试去获取锁,获取到之后才从这里继续执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// 另一个线程
public void notifyThread() {
    synchronized (lock) {
        lock.notify(); // 通知等待在lock上的线程
    }
}

比喻:WAITING就像你在医院等体检报告,医生让你“在外面等着,叫到你再进来”,你不知道要等多久。TIMED_WAITING就像你点了个外卖,APP显示“预计30分钟送达”,你就在这30分钟内边等边做别的事。


状态5:BLOCKED (阻塞) - “让我进去!!”

这个状态特指一种情况:小程想进入一个synchronized同步代码块(或方法),但这个代码块已经被其他线程(比如小A)占用了。

此时,小程不会像WAITING那样主动休息,而是会在门口摆出战斗姿势,不断地尝试冲击,直到拿到锁为止。在这个反复尝试的过程中,他的状态就是BLOCKED

// 假设这个锁已经被小A持有了
synchronized (lock) { // 小程执行到这里时,发现锁被占,状态变为BLOCKED
    buyMilkTea();     // 一旦拿到锁,状态就变回RUNNABLE,然后执行
}

BLOCKED vs WAITING 核心区别

  • BLOCKED 是“被动”的,是别人占着茅坑不拉屎,你没办法,只能硬等。
  • WAITING 是“主动”的,是你自己选择放弃CPU和锁,去休息,等别人通知你。

比喻:BLOCKED就像你想进唯一的单间厕所,但里面有人,你只能焦急地在门口踱步,不断尝试推门(竞争锁)。WAITING就像你发现单间有人,于是你把自己的电话号码(wait())给管理员,然后回座位玩手机去了,等管理员打电话(notify())叫你再来。


状态6:TERMINATED (终结) - 功成身退

当小程的run()方法执行完毕,或者中途抛出了未捕获的异常,他的生命就走到了尽头,进入TERMINATED状态。这个状态是不可逆的,一旦死亡,就不能再调用start()重启了(会抛异常)。

// ...小程的run方法执行完了...
System.out.println(xiaoCheng.getState()); // 输出 TERMINATED
xiaoCheng.start(); // 抛出 IllegalThreadStateException

比喻:任务完成,退休!或者因公殉职。


第三章:全景时序图 - 小程的一生

下面这张图描绘了小程一生中所有可能的状态变迁路径:

deepseek_mermaid_20250913_823bd1.png


第四章:源码视角 - 状态如何被管理

状态的变化在JVM底层(如Hotspot VM)是如何实现的呢?它和操作系统状态紧密相关。

  1. NEW & TERMINATED:纯粹是Java层面的管理,一个标记而已。
  2. RUNNABLE:在JVM中,对应着操作系统线程的就绪(Ready)  和运行(Running)  状态。调度的细节由操作系统负责。
  3. BLOCKED:当线程竞争synchronized管程锁(Monitor Lock)  失败时,JVM会通过一个叫ObjectMonitor的机制,将该线程放入一个叫做_EntryList的队列中排队,此时线程状态即为BLOCKED。
  4. WAITING/TIMED_WAITING:当线程调用wait()方法时,JVM会将其放入另一个叫做_WaitSet的队列中,并修改其状态。等待notify()时,再将其移回_EntryList去重新竞争锁。像sleep()这样的操作,则是由JVM向操作系统发起一个定时等待的请求来实现的。

底层奥秘:在Linux系统下,线程的挂起(如park()wait())和唤醒(unpark()notify())最终常常通过pthread_cond_waitpthread_cond_signal这类条件变量机制来实现。


总结与记忆窍门

记住这个故事和这个状态转换图,你就彻底掌握了Thread的状态:

  • NEWnew出来的蓝图。
  • RUNNABLEstart()后的万事俱备,只等CPU。
  • BLOCKED:抢**synchronized锁**失败,在门口干等。
  • WAITING主动休息,等别人无条件叫我 (wait()join())。
  • TIMED_WAITING主动休息,等一段时间或别人叫 (sleep(time)wait(timeout))。
  • TERMINATED:任务完成,寿终正寝。

希望小程买奶茶的奇幻之旅,能让你对Thread的状态有了深刻而有趣的理解,小程真的不是小小程序员的意思,希望各位大佬不要误会!