在Java中,线程是并发执行的基本单位,它允许程序同时执行多个任务。理解Java中的线程生命周期和状态转换对于有效地管理和控制多线程应用程序至关重要。Java线程的生命周期包括几个不同的状态,这些状态之间可以相互转换。以下是对Java线程生命周期和状态转换的详细解释:
线程生命周期状态
新建(New):
- 当线程实例被创建但尚未调用
start()
方法时,线程处于新建状态。在这个状态下,线程仅仅是一个对象,还没有开始执行。
- 当线程实例被创建但尚未调用
就绪(Runnable):
- 当调用线程的
start()
方法后,线程进入就绪状态。此时,线程已经准备好运行,但尚未被操作系统的调度器选中以分配CPU资源。在Java虚拟机(JVM)中,就绪状态通常被表示为RUNNABLE
,尽管它可能还没有实际运行。
- 当调用线程的
运行(Running):
- 当调度器选中处于就绪状态的线程并为其分配CPU资源时,线程开始执行并进入运行状态。线程在此状态下会在CPU上执行指令。
阻塞(Blocked):
- 线程可能会因为等待某些资源(如I/O操作完成或获取同步锁)而被阻塞。处于阻塞状态的线程不会执行任何指令,直到它等待的条件满足(如锁被释放或I/O操作完成)。
等待(Waiting):
- 线程进入等待状态是因为它调用了
Object.wait()
、Thread.join()
或LockSupport.park()
等方法。在等待状态的线程不会执行任何指令,直到另一个线程执行了相应的通知方法(如Object.notify()
或Object.notifyAll()
)。
- 线程进入等待状态是因为它调用了
计时等待(Timed Waiting):
- 线程进入计时等待状态是因为它调用了带有超时的
wait()
、join()
方法,或者使用了ScheduledExecutorService
等。计时等待状态的线程会在指定的时间等待,之后会转换为就绪状态。
- 线程进入计时等待状态是因为它调用了带有超时的
终止(Terminated):
- 当线程的
run()
方法执行完成后,线程进入终止状态。一旦线程进入终止状态,它就不能被重新启动。
- 当线程的
状态转换
- 新建 -> 就绪: 调用线程的
start()
方法。 - 就绪 -> 运行: 调度器选择该线程并分配CPU资源。
- 运行 -> 阻塞: 线程尝试获取一个已被其他线程持有的锁,或执行I/O操作等。
- 运行 -> 等待: 线程调用
wait()
、join()
或LockSupport.park()
等方法。 - 运行/阻塞/等待 -> 计时等待: 调用带有超时的
wait()
、join()
方法,或使用Thread.sleep()
等。 - 计时等待 -> 就绪: 超时时间到达或被
notify()
/notifyAll()
唤醒。 - 阻塞 -> 就绪: 锁被释放,I/O操作完成,或
sleep()
时间结束。 - 等待/计时等待 -> 终止: 被
notify()
/notifyAll()
唤醒,并且run()
方法执行完成。
总结
Java线程的生命周期和状态转换是由线程调度器和线程自身的行为共同决定的。理解这些状态和它们之间的转换关系对于开发多线程应用程序至关重要,因为它有助于确保线程的正确创建、管理和生命周期控制。在面试中,能够清晰地描述线程的各个状态和它们之间的转换关系,以及能够处理线程安全问题(如死锁、活锁等),都是重要的考察点。