文章目录
在学习Java多线程时,总是被这么多种状态以及其转换方法搞得很头大,今天这篇文章就来捋一捋Java多线程中的几种线程状态以及相应的转换方法!
在讲解Java线程状态前先来看看在现代操作系统中更加主流的线程状态和转换:
1. 现代操作系统中的线程状态及转换(5种)
在现在的操作系统中,线程是被视为轻量级进程的,所以操作系统线程的状态其实和操作系统进程的状态是一致的。具有以下 5 种状态:
- 创建态(new) :进程正在被创建,尚未到就绪状态。
- 就绪态(ready) :进程已处于准备运行状态(等待被调度),即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
- 运行态(running) :进程正在处理器上上运行(单核 CPU 下任意时刻只有一个进程处于运行状态)。
- 阻塞态(waiting) :又称为等待状态(等待资源),进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
- 结束/终止态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。
状态转换条件:
- 就绪态→运行态:处于就绪态的进程被调度后,获得处理机资源(分派处理机时间片),于是进程由就绪态转换为运行态。
- 运行态→就绪态:处于运行态的进程在时间片用完后,不得不让出处理机,从而进程由运行态转换为就绪态。此外,在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度程序将正在执行的进程转换为就绪态,让更高优先级的进程执行。
- 运行态→阻塞态:进程请求某一资源(如外设)的使用和分配或等待某一事件的发生(如 I/O 操作的完成)时,它就从运行态转换为阻塞态。进程以系统调用的形式请求操作系统提供服务,这是一种由运行用户态程序调用操作系统内核过程的形式。
- 阻塞态→就绪态:进程等待事件到来时,如 I/O 操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞态转换为就绪态。
应该注意以下内容:
- 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间片,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
- 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。
2. Java 线程状态(6种)
// Thread.State 源码
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
2.1 NEW 创建
处于 NEW 状态的线程此时尚未启动。这里的尚未启动指的是还没调用 Thread 实例的 start()
方法。
private void testStateNew() {
Thread thread = new Thread(() -> {
});
System.out.println(thread.getState()); // 输出 NEW
}
从上面可以看出,只是创建了线程而并没有调用 start()
方法,此时线程处于 NEW 状态。
关于 start()
的两个引申问题:
- 反复调用同一个线程的
start()
方法是否可行? - 假如一个线程执行完毕(此时处于 TERMINATED 状态),再次调用这个线程的
start()
方法是否可行?
要分析这两个问题,先来看看 start()
的源码:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
可以看到,在 start()
内部,这里有一个 threadStatus 的变量。如果它不等于 0,调用 start()
是会直接抛出异常的。
接着往下看,有一个 native 的 start0()
方法。这个方法里并没有对 threadStatus 的处理。可以通过 debug 的方式再看一下: