线程状态
每一个线程都有自己的局部变量表、程序计数器,以及生命周期等,线程的生命周期中各个阶段的状态包括:
NEW(初始化),
RUNNABLE(可运行的),
BLOCKED(锁定或阻塞),
WAITING(等待),
TIMED_WAITING(定时等待),
TERMINATED(结束);
在Thread内部类java.lang.Thread.State中定义了线程的生命周期中的所有状态
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
获取线程的状态方法:java.lang.Thread#getState
public State getState() {
// get current thread state 获取当前线程的线程状态
return sun.misc.VM.toThreadState(threadStatus);
}
各个状态详情介绍:
1. 新建状态(new):
NEW状态为使用new创建一个线程对象,在堆中分配内存空间在调用start方法之前的线程所处的状态;在此状态下,线程还没启动,只是创建了一个线程对象存储在堆中;比如:
Thread t = new Thread(); // 此时t就属于新建状态
当新建状态下的线程对象调用了start方法,该线程对象就从新建状态进入可运行状态(runnable);
线程对象的start方法只能调用一次,多次调用会发生IllegalThreadStateException
;
2. 可运行状态(runnable):
又可以细分成两种状态,ready
和running
,分别表示就绪状态和运行状态。将可运行状态细分为两种状态是因为不确定线程是否获取到CPU时间片。
就绪状态:线程对象调用start方法之后,等待JVM的调度(此时该线程并没有运行),还未开始运行;
运行状态:线程对象已获得JVM调度,处在运行中;如果存在多个CPU,那么允许多个线程并行运行;
3. 阻塞状态(blocked):
处于运行中的线程因为某些原因放弃CPU时间片,暂时停止运行,就会进入阻塞状态;此时JVM不会给线程分配CPU时间片,直到线程重新进入就绪状态(ready),才有可能转到运行状态;
阻塞状态只能先进入就绪状态,进而由操作系统转到运行状态,不能直接进入运行状态;
阻塞状态产生的情况:
- 当A线程处于运行中,试图获取同步锁时,但同步锁却被B线程获取,此时JVM会把A线程存到共享资源对象的锁池中,A线程进入阻塞状态;
- 当线程处于运行状态,发出了IO请求时,该线程会进入阻塞状态;
4. 等待状态(waiting):
运行中的线程调用了wait方法(无参数的wait方法),然后JVM会把该线程储存到共享资源的对象等待池中,该线程进入等待状态;处于该状态中的线程只能被其他线程唤醒(notify);
5. 计时等待状态(timed waiting):
运行中的线程调用了带参数的wait方法或者sleep方法,线程进入计时等待状态。
计时等待状态下的线程不会释放同步锁/同步监听器;
计时等待状态产生的情况:
- 当处于运行中的线程,调用了wait(long time)方法,JVM会把当前线程存在共享资源对象等待池中,线程进入计时等待状态;
- 当前线程执行了sleep(long time)方法,该线程进入计时等待状态;
6. 终止状态(terminated):
也可以称为死亡状态,表示线程终止,它的生命走到了尽头;线程一旦终止,就不能再重启启动,否则会发生IllegalThreadStateException;
终止状态产生的情况:
- 正常执行完run方法而退出,寿终正寝,属于正常死亡;
- 线程执行遇到异常而退出,线程中断,属于意外死亡;
线程的生命周期状态图:
线程状态上图解读:
- 一个线程在其被创建之后它将处于 NEW(新建)状态,调用runnable.start()方法后开始运行,这时线程将转换为RUNNABLE状态。RUNNABLE共有2种状态,就绪(READY)和运行中(RUNNING),当线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态,未获得就处于READY(就绪)状态。
- 在RUNNABLE状态,我们可以转换为waiting,time_waiting和blocked三种状态。
- 进入waiting状态可以执行wait()等方法,而waiting状态转换为running状态可以通过notify等方法。
- 进入time_waiting状态可以通过sleep(long millis)方法或 wait(long millis),返回runnbale状态则可以通过notify等方法。
- 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
- 线程在执行完Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
测试线程状态代码
package com.thread.study;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
public class TestThreadState {
public static void main(String[] args) throws InterruptedException {
final long ONE_SECONDS = 1000; // 1秒
Thread mark = new Thread(() -> { // --> NEW
synchronized (args) {
try {
blocking(ONE_SECONDS); // --> BLOCKED
args.wait(); // --> WAITING
args.wait(ONE_SECONDS); // --> TIMED_WAITING
} catch (InterruptedException e) {
}
}
},"测试状态的线程");
printThreadState(mark);
new Thread(() -> { // 执行使 mark 线程启动后进入监视器锁定阻塞的线程(等待获取锁)
try {
blocking(ONE_SECONDS + 500);
} catch (InterruptedException e) {
}
}).start();
Thread.sleep(ONE_SECONDS / 10);
mark.start(); // 启动 mark 线程 --> RUNNABLE
printThreadState(mark); // 打印 mark 线程 start() 后的状态
Thread.sleep(ONE_SECONDS / 10);
printThreadState(mark); // 打印 mark 线程 等待获取 sync 的状态
Thread.sleep(ONE_SECONDS * 2 + 500); // 等待 mark 线程 blocking() 执行完成后执行 wait()
printThreadState(mark); // 打印 mark 线程 wait() 时的状态
synchronized (args) {
args.notify(); // 唤醒 mark 线程
}
Thread.sleep(ONE_SECONDS / 2); // 等待 mark 线程执行 wait(long timeout)
printThreadState(mark); // 获取 mark 线程 wait(long timeout) 时的状态
Thread.sleep(ONE_SECONDS); // 等待 mark 线程执行完毕
printThreadState(mark); // 打印已执行完成的状态 --> TERMINATED
}
static synchronized void blocking(long timeout) throws InterruptedException {
Thread.sleep(timeout);
}
static void printThreadState(Thread stateThread) {
System.out.println(stateThread.getName() + DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN) + "的线程状态为" + stateThread.getState());
}
}
打印结果:
测试状态的线程2021-01-29 21:35:07.732的线程状态为NEW
测试状态的线程2021-01-29 21:35:07.935的线程状态为RUNNABLE
测试状态的线程2021-01-29 21:35:08.036的线程状态为BLOCKED
测试状态的线程2021-01-29 21:35:10.536的线程状态为WAITING
测试状态的线程2021-01-29 21:35:11.037的线程状态为TIMED_WAITING
测试状态的线程2021-01-29 21:35:12.039的线程状态为TERMINATED
网友评论