☰
Java线程生命周期全解析:从NEW到TERMINATED!
2026/9/30 11:00:14 网站建设 项目流程

全文目录:

    • 开篇语
    • 一、线程生命周期与状态转换
      • 1. NEW:刚创建,还没“开工”
      • 2. RUNNABLE:正在 CPU 上排队 / 跑着
      • 3. BLOCKED:等着进“临界区”的锁
      • 4. WAITING:无限期等待某个条件
      • 5. TIMED_WAITING:带闹钟的等待
      • 6. TERMINATED:线程结束了
      • 7. 状态转换小总结(文字版“状态图”)
    • 二、Thread vs Runnable vs Callable:谁负责干活?谁只是“任务说明书”?
      • 1. Thread:线程本体 + 可选“任务”
      • 2. Runnable:没有返回值的“任务接口”
      • 3. Callable:有返回值的任务接口(配合 Future)
    • 三、sleep() vs wait():都“睡觉”,但一个只是打盹,一个是“放弃座位排队重来”
      • 1. 来个对比大表先压压惊
      • 2. sleep():谁也不管,我就静静躺一会儿
      • 3. wait():把锁先让出来,等别人叫我再继续
      • 4. 一个经典生产者-消费者对比示例
        • 使用 `wait()` / `notify()` 实现简单阻塞队列
      • 5. sleep() vs wait() 再来几句“人话”版总结
    • 四、最后帮你串一下三块知识
    • 文末

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

一、线程生命周期与状态转换

Java 里线程的状态是Thread.State这个枚举定义的,常见的就六个:

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED

你可以把一个线程想象成人在公司上班的一天:刚入职、排队打卡、占用会议室、等别人开会、定时闹钟、最后下班走人。

1. NEW:刚创建,还没“开工”

Threadt=newThread(()->{System.out.println("running...");});System.out.println(t.getState());// NEW
  • 只调用了new Thread(...),还没start()
  • 仅仅是个“Java 对象”,还没有真正对应到底层系统线程

2. RUNNABLE:正在 CPU 上排队 / 跑着

start()之后,线程进入RUNNABLE:

t.start();System.out.println(t.getState());// 通常是 RUNNABLE

注意:

  • 在 Java 里,RUNNABLE包含了“就绪 + 运行中”两种状态
  • 具体是正在 CPU 上跑,还是在就绪队列里等,JVM 不对你细分

典型进入RUNNABLE的路径:

  • 从NEW调用start()
  • 从BLOCKED拿到锁
  • 从WAITING/TIMED_WAITING被唤醒且拿到锁
  • 从 I/O 阻塞恢复(某些实现会表现为 RUNNABLE)

3. BLOCKED:等着进“临界区”的锁

BLOCKED专门表示:

线程在等待一个 synchronized 的监视器锁(intrinsic lock)。

例如:

finalObjectlock=newObject();Threadt1=newThread(()->{synchronized(lock){try{Thread.sleep(100000);}catch(InterruptedExceptione){}}});Threadt2=newThread(()->{synchronized(lock){System.out.println("t2 got lock");}});t1.start();Thread.sleep(100);// 让 t1 先拿到锁t2.start();Thread.sleep(100);System.out.println(t2.getState());// 可能是 BLOCKED

这里:

  • t1先进入 synchronized 块,并sleep,一直占着锁
  • t2想进 synchronized 块,发现锁被占用 →BLOCKED
  • 当t1出 synchronized,t2才能从BLOCKED→RUNNABLE

4. WAITING:无限期等待某个条件

WAITING表示:

线程在无限期等待,直到被别的线程显式唤醒。

常见导致WAITING的方法:

  • Object.wait()(无超时)
  • Thread.join()(无超时)
  • LockSupport.park()

例子(wait / notify):

finalObjectlock=newObject();Threadt=newThread(()->{synchronized(lock){try{lock.wait();// 线程进入 WAITING,并且释放锁System.out.println("被唤醒啦");}catch(InterruptedExceptione){}}});t.start();Thread.sleep(100);System.out.println(t.getState());// WAITINGsynchronized(lock){lock.notify();}

特点:

  • 线程会释放锁(和BLOCKED不同)
  • 必须等到别人notify()/notifyAll()/ interrupt 才可能继续

5. TIMED_WAITING:带闹钟的等待

TIMED_WAITING表示:

线程在有“时间上限”的等待:时间到了会自动醒。

典型来源:

  • Thread.sleep(millis)
  • Object.wait(timeout)
  • 带超时的Thread.join(timeout)
  • LockSupport.parkNanos()/parkUntil()

例子:

Threadt=newThread(()->{try{Thread.sleep(10000);// 10 秒}catch(InterruptedExceptione){}});t.start();Thread.sleep(100);System.out.println(t.getState());// TIMED_WAITING
  • 超时到了,线程从TIMED_WAITING→RUNNABLE
  • 如果是wait(timeout),还需要重新拿锁才能继续执行

6. TERMINATED:线程结束了

当run()或call()执行完,线程就进入TERMINATED:

Threadt=newThread(()->{System.out.println("done");});t.start();t.join();// 等它结束System.out.println(t.getState());// TERMINATED

7. 状态转换小总结(文字版“状态图”)

可以把线程的一生理解为:

  • NEW→(start)→RUNNABLE

  • RUNNABLE→

    • 进入synchronized前抢锁失败 →BLOCKED
    • 调用wait()/join()/park()→WAITING
    • 调用sleep()/wait(timeout)/join(timeout)/parkNanos→TIMED_WAITING
  • BLOCKED/WAITING/TIMED_WAITING→

    • 拿到锁、被唤醒、超时、被中断 等 → 回到RUNNABLE
  • 最终,run 执行完 →TERMINATED

二、Thread vs Runnable vs Callable:谁负责干活?谁只是“任务说明书”?

你可以这么理解:

  • Runnable/Callable:任务说明书
  • Thread:真正的“工人”
  • 线程池(ExecutorService):工头 + 员工队列

1. Thread:线程本体 + 可选“任务”

Thread是一个类,代表一个线程对象,它内部有一个run()方法:

  • 可以直接继承Thread,重写run()
  • 也可以把Runnable丢进去,让它帮你跑

示例 1:继承 Thread(不太推荐)

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("I am a Thread");}}newMyThread().start();

问题:类已经被占用,不能再继承其它父类,可扩展性较差。

2. Runnable:没有返回值的“任务接口”

Runnable是一个函数式接口,只定义了一个void run():

Runnabletask=()->{System.out.println("running in "+Thread.currentThread().getName());};newThread(task).start();

特点:

  • 没有返回值
  • 不能抛 checked exception(只能自己 try-catch)
  • 适合那些只需要“干完就完了”的任务

在使用线程池时,execute(Runnable command)就是跑这种任务。

3. Callable:有返回值的任务接口(配合 Future)

Callable<V>是比Runnable更“高级”一点的任务描述:

  • 方法是V call() throws Exception;
  • 有返回值
  • 可以抛 checked exception

典型用法:配合ExecutorService.submit()和Future使用:

importjava.util.concurrent.*;Callable<Integer>task=()->{Thread.sleep(1000);return42;};ExecutorServicepool=Executors.newFixedThreadPool(2);Future<Integer>future=pool.submit(task);System.out.println("doing something else...");Integerresult=future.get();// 阻塞等待结果System.out.println("result = "+result);pool.shutdown();

对比一下:

对象类型核心方法返回值是否能抛 checked 异常常见使用场景
Thread类run()/start()void不支持(run 不能抛)直接表示一个线程
Runnable函数式接口run()void不支持提交到线程池、给 Thread 跑
Callable函数式接口call()任意类型V支持有返回值、有异常的异步任务

一句直白总结:

  • 想要返回值?用Callable+Future。
  • 不需要返回值,只是跑一下逻辑?Runnable就够了。
  • 不要随便 new Thread,优先线程池。

三、sleep() vs wait():都“睡觉”,但一个只是打盹,一个是“放弃座位排队重来”

这俩名字都很迷惑新人,但本质区别非常关键。

1. 来个对比大表先压压惊

对比点Thread.sleep()Object.wait()
所在类ThreadObject
是否释放锁❌ 不释放锁✅ 释放对象监视器锁(synchronized 的那个锁)
是否必须在 synchronized 里调用❌ 不要求✅ 必须持有该对象的 monitor,否则抛IllegalMonitorStateException
唤醒方式时间到了 / 被中断notify()/notifyAll()/ 时间到 / 被中断
典型用途单纯让线程暂停一段时间,不用于通信线程间协作 / 条件等待(生产者-消费者等)
所属状态通常进入TIMED_WAITINGWAITING或TIMED_WAITING

2. sleep():谁也不管,我就静静躺一会儿

try{Thread.sleep(1000);// 当前线程睡 1 秒}catch(InterruptedExceptione){// 响应中断}

特点:

  1. 当前线程进入TIMED_WAITING
  2. 不会释放已经占有的任何锁(比如你在 synchronized 里 sleep)
  3. 时间到后自动恢复到RUNNABLE
  4. 中途被interrupt()会抛InterruptedException,并清除中断标志

比如:

synchronized(lock){Thread.sleep(10000);// 期间别人拿不到这个 lock}

这就是为什么在synchronized里长时间sleep()是个风险操作:
你拿着锁睡觉,别人全被堵在外面。

3. wait():把锁先让出来,等别人叫我再继续

wait()是配合 synchronized 使用的线程协作机制。

finalObjectlock=newObject();Threadt1=newThread(()->{synchronized(lock){try{System.out.println("t1: wait before");lock.wait();// 1. 释放 lockSystem.out.println("t1: resumed");}catch(InterruptedExceptione){}}});Threadt2=newThread(()->{synchronized(lock){System.out.println("t2: notify");lock.notify();}});

关键行为:

  1. 调用lock.wait()前,当前线程必须已经持有lock的锁(在 synchronized 块/方法中)

  2. 调用wait()后:

    • 当前线程进入WAITING或TIMED_WAITING
    • 释放该对象的锁,让别的线程可以拿到这个锁干活
  3. 其他线程在同一个对象锁上调用notify()/notifyAll()后:

    • 等待中的线程会被唤醒,重新去抢这个锁
    • 抢到锁之后,从wait()之后继续执行

4. 一个经典生产者-消费者对比示例

使用wait()/notify()实现简单阻塞队列
classMyBlockingQueue{privatefinalQueue<Integer>queue=newLinkedList<>();privatefinalintcapacity;publicMyBlockingQueue(intcapacity){this.capacity=capacity;}publicvoidput(intvalue)throwsInterruptedException{synchronized(queue){while(queue.size()==capacity){queue.wait();// 队列满了,释放锁,等待消费者消费}queue.add(value);queue.notifyAll();// 通知可能在等的消费者}}publicinttake()throwsInterruptedException{synchronized(queue){while(queue.isEmpty()){queue.wait();// 队列空了,释放锁,等生产者放数据}intval=queue.poll();queue.notifyAll();// 通知可能在等的生产者returnval;}}}

这里用的是典型的条件等待 + 释放锁的模式,sleep()完全干不了这事。

如果你用sleep()来轮询队列是否为空,不仅浪费 CPU(频繁检查),还不精确(时间控制难)。

5. sleep() vs wait() 再来几句“人话”版总结

  • sleep():

    “我就在这儿等一会儿,谁也别动我的东西(锁),时间到了我自己继续跑。”

  • wait():

    “我先把座位(锁)让出来,大家先忙,等你们叫我(notify)我再抢座回来继续干。”

所以:

  • 想要简单延时:sleep()
  • 想要线程之间协作 / 条件同步:wait()/notify()(或更现代的 Lock / Condition / BlockingQueue 等)

四、最后帮你串一下三块知识

如果把刚才三块揉成一个脑图:

  • 线程状态

    • 启动:NEW → RUNNABLE
    • 等锁:BLOCKED(synchronized)
    • 等条件 / 等别人叫:WAITING / TIMED_WAITING(wait/join/park/sleep)
    • 结束:TERMINATED
  • 任务 vs 线程

    • Runnable/Callable= 任务描述
    • Thread= 真正线程
    • 线程池 = 管线程 + 执行任务
  • sleep vs wait

    • 都会让线程进入TIMED_WAITING
    • 最大差别:是否释放锁&是否需要别人“叫醒”
    • sleep:不管别人,就自己暂停
    • wait:用于线程协调,释放锁,配合notify/notifyAll

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询