一文读懂Java线程状态转换

时间:2023-12-28 20:50:02

前言

本文描述Java线程线程状态及状态转换,不会涉及过多理论,主要以代码示例说明线程状态如何转换。

基础知识

1. 线程状态

Thread源码中的状态说明:

一文读懂Java线程状态转换

线程可以有6种状态:

  • New(新建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed waiting(计时等待)
  • Terminated(被终止)
  1. New:new Thread()后线程的状态就是新建。
  2. Runnable:线程一旦调用start()方法,无论是否运行,状态都为Runable,注意Runable状态指示表示线程可以运行,不表示线程当下一定在运行,线程是否运行由虚拟机所在操作系统调度决定。
  3. 被阻塞:线程试图获取一个内部对象的Monitor(进入synchronized方法或synchronized块)但是其他线程已经抢先获取,那此线程被阻塞,知道其他线程释放Monitor并且线程调度器允许当前线程获取到Monitor,此线程就恢复到可运行状态。
  4. 等待:当一个线程等待另一个线程通知调度器一个条件时,线程进入等待状态。
  5. 计时等待:和等待类似,某些造成等待的方法会允许传入超时参数,这类方法会造成计时等待,收到其他线程的通知或者超时都会恢复到可运行状态。
  6. 被终止:线程执行完毕正常结束或执行过程中因未捕获异常意外终止都会是线程进入被终止状态。

2. 线程状态转换

线程从“新建”到“被终止”会历经多次状态转换,所有可能的转换如下图:

一文读懂Java线程状态转换

【图一】

观察状态转化图,我们发现“可运行”状态为所有状态的必经状态。我们分析出四条基本的状态转换线路图。

  • 新建--->可运行--->被终止
  • 新建--->可运行--->被阻塞--->可运行--->被终止
  • 新建--->可运行--->等待--->可运行--->被终止
  • 新建--->可运行--->计时等待--->可运行--->被终止

“新建”和“被终止”状态分别为起始和结束状态,和“可运行”状态不可逆。其他状态均能和“可运行”状态相互转换。

用代码说话

让我们用代码演示线程状态是如何转换的,大家重点关注两个问题?

  • 什么操作会改变线程状态?
  • 改变的状态是如何恢复的?

一、 新建--->可运行--->被终止

这个状态转换时创建的线程生命周期。

/**
* NEW->RUNNABLE->TERMINATED
*/
public class ThreadStateNRT { public static void main(String[] args) {
Thread thread=new Thread(new Task());
print(thread.getName(),thread.getState());
thread.start();
//等待线程执行完毕。
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
print(thread.getName(),thread.getState());
} private static class Task implements Runnable{
@Override
public void run() {
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
private static final String stringFormat="%s:%s";
private static void print(String threadName,Thread.State state){
System.out.println(String.format(stringFormat,threadName,state));
}
}

其中,print()方法用来打印线程信息。后面的代码示例均不在展示。

运行程序结果为:

Thread-0:NEW 
Thread-0:RUNNABLE 
Thread-0:TERMINATED

二、 新建--->可运行--->被阻塞--->可运行--->被终止

只有一种方法能出现阻塞状态,那就是synchronized同步原语。我们需要两个线程其中一个线程被另一个阻塞来展示。

/**
* NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
*/
public class ThreadStateNRBRT {
//锁
private static final Object lock=new Object(); public static void main(String[] args) {
//辅助线程,制造synchronized状态。
Thread assistantThread = new Thread(new SynTask());
assistantThread.start();
try {
//保证assistantThread先执行。
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread showThread = new Thread(new Task());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(),showThread.getState());
//因为无法判断显示线程何时执行,所以循环直到显示线程执行。
while (true){
if(showThread.getState()==Thread.State.BLOCKED){
print(showThread.getName(), Thread.State.BLOCKED);
break;
}
}
//等待两个线程执行完毕。
try {
assistantThread.join();
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class SynTask implements Runnable {
@Override
public void run() {
//锁定一定时间
synchronized (lock){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} private static class Task implements Runnable {
@Override
public void run() {
synchronized (lock){
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
}
}

执行一下你有可能看到正确结果:

Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:BLOCKED
Thread-1:RUNNABLE
Thread-1:TERMINATED

为什么是有可能呢?我们调整一下代码,例如将加锁的时间调小一点:

private static class SynTask implements Runnable {
@Override
public void run() {
//锁定一定时间
synchronized (lock){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

注意此处Thread.sleep(10),我们只锁住十毫秒。

再运行一下,控制台可能打印出这样的结果且程序不会结束:

Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:RUNNABLE

造成以上结果的原因是我么无法保证两个线程的执行顺序,也无法证主线程一定能打印出显示线程阻塞的状态。

         while (true){
if(showThread.getState()==Thread.State.BLOCKED){
print(showThread.getName(), Thread.State.BLOCKED);
break;
}
}

所以执行在这段代码死循环了。

调整一下代码,保证不会因为参数调整改变线程之间的执行顺序和打印结果。

/**
* NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
*/
public class ThreadStateNRBRT_New {
//锁
private static final Object lock=new Object();
//锁定标志
private volatile static boolean lockFlag=true;
//执行顺序
private volatile static int order=0; public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new Task());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(), showThread.getState());
//辅助线程,制造synchronized状态。
Thread assistantThread = new Thread(new SynTask());
assistantThread.start(); //循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
while (true){
if(showThread.getState()==Thread.State.BLOCKED){
print(showThread.getName(), Thread.State.BLOCKED);
lockFlag=false;
break;
}
} try {
assistantThread.join();
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class SynTask implements Runnable {
@Override
public void run() {
while (true) {
//保证先进入同步范围。
if (order == 0) {
synchronized (lock) {
//启动另一个同步
order=1;
//等待主线程读取到线程阻塞状态,退出同步。
while (lockFlag) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
break;
}
}
}
} private static class Task implements Runnable {
@Override
public void run() {
while (true){
//保证后进入同步范围。
if (order==1){
synchronized (lock){
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
break;
}
}
}
}
}

我们用order保证线程进入同步区的顺序,用lockFlag保证只有在打印出显示线程的被阻塞状态后辅助线程才退出同步区。这样无论如何执行我们都会得到同样的结果。

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:BLOCKED
Thread-0:RUNNABLE
Thread-0:TERMINATED

三、 新建--->可运行--->等待--->可运行--->被终止

这里我们展示两种三种方法造成线程的等待状态

  • Object.wait()
  • java.util.concurrent.locks.Locke.lock()
  • java.util.concurrent.locks.Condition.await()

其他方法如Thread.join()等大家可以参考示例代码自己实现。

1. Object.wait()

/**
* NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
*/
public class ThreadStateNRWRT {
//锁
private static final Object lock=new Object(); public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new WaitTask());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(),showThread.getState());
//循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
while (true){
if(showThread.getState()==Thread.State.WAITING){
print(showThread.getName(), Thread.State.WAITING);
break;
}
}
synchronized (lock){
lock.notify();
} try {
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class WaitTask implements Runnable {
@Override
public void run() {
//等待
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
}

2. java.util.concurrent.locks.Locke.lock()

/**
* NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
*/
public class ThreadStateNRWRTLock {
//锁
private static Lock lock=new ReentrantLock();
//锁定标志
private volatile static boolean lockFlag=true;
//执行顺序
private volatile static int order=0; public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new Task());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(), showThread.getState());
//辅助线程,制造synchronized状态。
Thread assistantThread = new Thread(new SynTask());
assistantThread.start();
//循环读取展示线程状态,直到读到展示线程状态为BLOCKED,才让辅助线程退出同步。
while (true){
if(showThread.getState()==Thread.State.WAITING){
print(showThread.getName(), Thread.State.WAITING);
lockFlag=false;
break;
}
}
try {
assistantThread.join();
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class SynTask implements Runnable {
@Override
public void run() {
while (true) {
//保证先进入同步范围。
if (order == 0) {
//加锁
lock.lock();
try {
order=1;
while (lockFlag) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock();
}
break;
}
}
}
} private static class Task implements Runnable {
@Override
public void run() {
while (true){
//保证后进入同步范围。
if (order==1){
lock.lock();
try{
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}finally {
lock.unlock();
}
break;
}
}
}
}
}

3. java.util.concurrent.locks.Condition.await()

/**
* NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
*/
public class ThreadStateNRWRTCondition {
//锁
private static Lock lock=new ReentrantLock();
private static Condition condition=lock.newCondition(); public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new WaitTask());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(),showThread.getState());
//循环读取展示线程状态,直到读到展示线程状态为WAITING,才让辅助线程唤醒等待线程。
while (true){
if(showThread.getState()==Thread.State.WAITING){
print(showThread.getName(), Thread.State.WAITING);
break;
}
} lock.lock();
try{
condition.signal();
}finally {
lock.unlock();
} try {
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class WaitTask implements Runnable {
@Override
public void run() {
//等待
lock.lock();
try{
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();
}
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
}

4. 运行结果

三段代码的运行结果都是:

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED

四、新建--->可运行--->计时等待--->可运行--->被终止

我们展示两个方法造成计时等待状态

  • Object.wait(long timeout)
  • java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)

其他方法如Thread.sleep(long millis),Thread.join(long millis)等大家可以自己实现。
感觉凡是有超时方法的方法都能让线程状态进入计时等待,但是这个没有经过验证,所以只是一个猜想。

1. Object.wait(long timeout)

/**
* NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
*/
public class ThreadStateNRTWRT {
//锁
private static final Object lock=new Object(); public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new WaitTask());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(),showThread.getState());
//循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
while (true){
if(showThread.getState()==Thread.State.TIMED_WAITING){
print(showThread.getName(), Thread.State.TIMED_WAITING);
break;
}
} try {
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class WaitTask implements Runnable {
@Override
public void run() {
//等待
synchronized (lock){
try {
lock.wait(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
}

2. java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)

/**
* NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED
*/
public class ThreadStateNRTWRTCondition {
//锁
private static Lock lock=new ReentrantLock();
private static Condition condition=lock.newCondition(); public static void main(String[] args) {
//展示线程
Thread showThread = new Thread(new WaitTask());
print(showThread.getName(), showThread.getState());
showThread.start();
print(showThread.getName(),showThread.getState());
//循环读取展示线程状态,直到读到展示线程状态为TIMED_WAITING。
while (true){
if(Thread.State.TIMED_WAITING==showThread.getState()){
print(showThread.getName(), Thread.State.TIMED_WAITING);
break;
}
}
try {
showThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程执行完毕打印状态。
print(showThread.getName(), showThread.getState());
} private static class WaitTask implements Runnable {
@Override
public void run() {
//等待
lock.lock();
try{
try {
condition.await(1,TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();
}
print(Thread.currentThread().getName(),Thread.currentThread().getState());
}
}
}

3. 运行结果

两段程序的运行结果相同:

Thread-0:NEW
Thread-0:RUNNABLE
Thread-0:TIMED_WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED

结语

至此,我们已经介绍了线程状态转换的所有情况,了解线程状态转换对分析多线程代码运行很帮助。希望本篇文章对大家今后工作有所助力。

参考

《Java核心技术+卷1》第九版

转自:https://segmentfault.com/a/1190000016197831?utm_source=tag-newest