前言
网上有很多关于Java线程生命周期
的文章,大部分都说Java线程有5种状态,你认可他们的说法吗?反正我不认可,java中的线程实际上有6种状态。贴出证据:
1 | public enum State { |
Thread.class 中定义了线程状态的State枚举类.
官方的注释:A thread can be in only one state at a given point in time. These states are virtual machine states which do not reflectany operating system thread states.
翻译出来为:一个线程在某一时刻只能有一种状态,这些状态是虚拟机状态,它不反映任何操作系统的线程状态。
再看一下RUNNABLE状态的注释:A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.
翻译出来为:处于runnable状态下的线程正在Java虚拟机中执行,但它可能正在等待操作系统的其他资源,比如处理器。
Java中的RUNNABLE状态实际上包含了Ready与Running的状态,所以你可以完全无视网上那些不准确的说法,这种问题的答案往往就在源码与javadoc中。
接下来,我们将详细讨论Java的核心概念-线程的生命周期。
Java多线程
在Java语言中,多线程由Thread的核心概念驱动。在其生命周期中,线程会经历各种状态:
Java线程的生命周期
java.lang.Thread类含一个State状态枚举。在任何给定的时间点,线程只能处于以下某一个状态:
- NEW: 新创建的尚未开始执行的线程
- RUNNABLE: 正在运行或正在等待资源准备执行
- BLOCKED: 等待获取锁以进入或重新进入同步块或方法
- WAITING: 没有时间限制地等待其他线程执行特定操作
- TIMED_WAITING: 在指定时间段内等待其他线程执行特定操作
- TERMINATED: 执行完毕
New
一个新线程是指已创建但尚未启动的线程。它保持这种状态,直到我们使用start()方法启动它为止。
1 | Runnable runnable = new NewState(); |
由于我们尚未启动提到的线程,因此方法t.getState()会输出:
1 | NEW |
Runnable
当我们创建了一个新线程并调用start()方法时,它会从NEW变为RUNNABLE状态。处于此状态的线程正在运行或可运行(正在等待系统分配资源)。
在多线程环境中,线程调度程序为每个线程分配固定的时间。因此它会运行特定的时间,然后将资源让给其他RUNNABLE线程。
例如,让我们在之前的代码中添加t.start()方法,然后尝试访问其当前状态:
1 | Runnable runnable = new NewState(); |
此代码极大概率会输出:
1 | RUNNABLE |
请注意,在此示例中,并不能保证在执行t.getState()时,它仍然处于RUNNABLE状态。
可能是线程调度程序立即对其进行了调度,并已经完成了执行。在这种情况下,我们可能会得到不同的输出。
Blocked
当前线程等待锁并尝试访问被其他线程持有锁的一段代码时,它将进入此状态。
1 | public class BlockedState { |
解释代码:
- 我们创建了两个不同的线程– t1和t2
- 定义了一个同步的commonResource方法,进入该方法的线程将无限循环,也就是持有锁的线程将不会释放锁
- 当t1进入此方法时,它将保持无限循环。
- 现在,我们启动t2时,它将尝试访问commonResource()方法,该方法已经被t1访问,因此,t2将保持在BLOCKED状态
在这种状态下,我们调用t2.getState()并获得以下输出:
1 | BLOCKED |
Waiting
一个线程在等待其他线程执行特定操作时处于WAITING状态。 根据JavaDocs的说法,任何线程都可以通过调用以下任何一种方法进入此状态:
- object.wait()
- thread.join()
- LockSupport.park()
请注意,在wait()和join()中,我们没有定义任何超时期限,下一节将介绍这种情况。
1 | public class WaitingState implements Runnable { |
解释代码:
- 我们创建并开始了t1
- t1创建一个t2并启动它
- 当t2执行时,我们调用t2.join(),这会将t1置于等待状态,直到t2完成执行
- 由于t1等待t2完成,因此我们在t2中调用t1.getState()
如您所料,这里的输出是:
1 | WAITING |
Timed Waiting
当一个线程在规定的时间内等待另一个线程执行特定操作时,该线程处于TIMED_WAITING状态。
根据JavaDocs的介绍,有五种方法可以将线程置于TIMED_WAITING状态:
- thread.sleep(long millis)
- wait(int timeout) or wait(int timeout, int nanos)
- thread.join(long millis)
- LockSupport.parkNanos
- LockSupport.parkUntil
1 | public class TimedWaitingState { |
我们创建并启动了线程t1,该线程进入睡眠状态的超时时间为5秒。输出将是:
1 | TIMED_WAITING |
Terminated
完成执行或异常终止时,它处于TERMINATED状态。
1 | public class TerminatedState implements Runnable { |
在这里,当我们启动线程t1时,下一条语句Thread.sleep(1000)为t1的完成提供了足够的时间,因此该程序将输出显示为:
1 | TERMINATED |
结论
通过本文,我们了解了Java线程的生命周期。讲解了Thread.State枚举定义的六个状态,并通过示例演示出来。文中有不对的地方,欢迎大家指正,谢谢!