一. Java 线程生命周期概述
Java 中的线程生命周期主要分为以下五个状态:
- 新建状态(NEW):线程被创建但尚未启动。
- 可运行状态(RUNNABLE):线程可以被操作系统调度执行。
- 阻塞状态(BLOCKED):线程正在等待获取锁,无法继续执行。
- 等待状态(WAITING):线程等待另一个线程显式地唤醒它。
- 超时状态(TIMED_WAITING):线程在等待指定时间后可以继续执行。
- 终止状态(TERMINATED):线程已经执行完毕或因异常终止。
二. 线程生命周期图示
这里借用一下二哥的图片
在上图中,我们可以清晰地看到 Java 线程生命周期中各个状态及它们之间的转换关系。
三. 各个状态的详细解释
1. 新建状态(NEW)
当线程对象被创建时,线程处于新建状态。这时,线程尚未开始执行。可以通过调用 new Thread()
或 new Thread(Runnable target)
来创建新线程。此时,线程还没有开始运行,需要调用 start()
方法才能进入下一阶段。
2. 可运行状态(RUNNABLE)
线程调用了 start()
方法后,进入可运行状态。在该状态下,线程可能正在运行,也可能在等待操作系统的调度。Java 中的 RUNNABLE
包括了操作系统中的就绪(Ready)和运行(Running)状态。线程在这个状态下能够获得 CPU 时间片并执行任务。
3. 阻塞状态(BLOCKED)
当一个线程试图获取一个已经被其他线程占用的锁时,它会进入阻塞状态。在这种情况下,线程无法继续执行,直到锁被释放。
阻塞状态会严重影响多线程程序的性能,因此合理的锁管理对于并发编程至关重要。
4. 等待状态(WAITING)
线程调用 Object.wait()
或 Thread.join()
方法时,进入等待状态。此时,线程等待其他线程通过 notify()
或 notifyAll()
方法显式唤醒它。
在 WAITING
状态下,线程不消耗 CPU 时间片,但仍然被认为是活动线程。
5. 超时等待状态(TIMED_WAITING)
线程可以在调用带有超时参数的方法时进入超时等待状态,比如 Thread.sleep()
或 Object.wait(long timeout)
。在这种状态下,线程会等待指定时间,然后自动唤醒。
线程在超时等待状态结束后,会重新进入可运行状态。
6. 终止状态(TERMINATED)
当线程的 run()
方法执行完成或遇到异常而退出时,线程进入终止状态。此时,线程生命周期结束,无法再次启动。
在终止状态下,线程的所有资源都会被回收,无法再次进入其他状态。
四. 状态转换的关键方法
Java 中,线程状态的转换由一系列方法控制。以下是一些常用方法及其对应的状态转换:
start()
:从 新建状态 进入 可运行状态。wait()
:从 可运行状态 进入 等待状态。sleep()
:从 可运行状态 进入 超时等待状态。join()
:从 可运行状态 进入 等待状态,直到指定线程执行完成。notify()
/notifyAll()
:从 等待状态 返回 可运行状态。- 线程执行完毕 或 异常退出:从 可运行状态 进入 终止状态。
五. 线程生命周期中的常见问题
-
线程阻塞问题:在阻塞状态下,线程长时间无法获得锁,会导致性能下降,甚至死锁。使用
ReentrantLock
等更灵活的锁机制可以避免这种问题。 -
线程资源消耗:线程在超时等待或等待状态下虽然不占用 CPU,但仍然消耗系统资源,频繁创建和销毁线程会增加系统开销。可以通过线程池(如
ExecutorService
)来管理线程的创建和回收。 -
线程同步问题:线程之间共享数据时需要同步操作,以避免竞争条件。可以使用
synchronized
关键字、Lock
接口或其他同步机制。
六. 总结
Java 线程的生命周期由多个状态组成,线程可以在这些状态之间进行转换。理解线程的生命周期不仅有助于编写高效的并发程序,还能有效防止死锁、线程阻塞等常见问题。另外java的线程状态和操作系统的线程状态有些许不同,大家可以结合记忆防止混淆。