前言

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

基础知识

1. 线程状态

Thread源码中的状态说明:

线程可以有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. 线程状态转换

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

【图一】

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

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

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

用代码说话

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

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

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

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

/**
* 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

一文读懂Java线程状态转换的更多相关文章

  1. Java线程状态转换

    前言:对于Java线程状态方面的知识点,笔者总感觉朦朦胧胧,趁着最近整理资料,将Java线程状态方面的知识点总结归纳,以便加深记忆. 1.Java线程状态值 在Thread类源码中通过枚举为线程定义了 ...

  2. 浅谈 Java线程状态转换及控制

    线程的状态(系统层面) 一个线程被创建后就进入了线程的生命周期.在线程的生命周期中,共包括新建(New).就绪(Runnable).运行(Running).阻塞(Blocked)和死亡(Dead)这五 ...

  3. 一文读懂Java动态代理

    作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...

  4. 一文读懂JAVA多线程

    背景渊源 摩尔定律 提到多线程好多书上都会提到摩尔定律,它是由英特尔创始人之一Gordon Moore提出来的.其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍 ...

  5. 一文搞懂 Java 线程中断

    在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程的方法吗?答案是肯定的,它就是我们今天要分 ...

  6. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  7. JAVA 线程状态转换图示及说明

    线程状态类型 新建状态(New):新创建了一个线程对象. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获取C ...

  8. 别指望一文读懂Java并发之从一个线程开始

    Understanding concurrent programming is on the same order of difficulty as understanding object-orie ...

  9. [No0000196]一文读懂Java 11的ZGC为何如此高效

    导读:GC是大部分现代语言内置的特性,Java 11 新加入的ZGC号称可以达到10ms 以下的 GC 停顿,本文作者对这一新功能进行了深入解析.同时还对还对这一新功能带来的其他可能性做了展望.ZGC ...

随机推荐

  1. Linux上安装nginx

    环境:centos7   nginx1.16.1(源码安装) 一.下载nginx源码包 地址:http://nginx.org/en/download.html Mainline version(主线 ...

  2. rsync 服务端和客户端 简单配置

    环境:Centos 6.9 两台服务器,A(192.168.223.129) 和 B(192.168.223.130).A 作为服务端,B作为客户端从A服务器同步目录.把A的/usr/src 目录下的 ...

  3. 数据库-mysql01 简单介绍以及安装部署

    本次mysql数据库安装采用二进制安装(免安装即绿色版),数据库版本是mysql5.7.26 首先下载mysql安装包,然后上传服务器里,最后解压. 卸载centos7自带的数据库软件包: [root ...

  4. ubuntu 16.04下node和pm2安装

    一.安装node,这里安装9.0的版本,安装其它版本直接到https://deb.nodesource.com/setup_9.x找相应版本的更改既可 1.sudo apt-get remove no ...

  5. 【洛谷P3756】[CQOI2017]老C的方块(最小割)

    洛谷 题意: 给出一个网格图类似于这样: 现在给出一个\(n*m\)大小的网格,之后会给出一些点,若某些点相连形成了如下的几个图案,那么就是不好的. 现在可以删去一些点,但删除每个点都有一些代价,问最 ...

  6. 201871010128-杨丽霞《面向对象程序设计(java)》第十四周学习总结

    201871010128-杨丽霞<面向对象程序设计(java)>第十四周学习总结(1分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-dai ...

  7. AsyncTask隐藏的陷阱

    转自:http://blog.csdn.net/snow4dev/article/details/8809897 当AsyncTask被介绍到Android中时,它被贴上“无忧线程”的标签.其目标是让 ...

  8. 洛谷 U87561 魔法月饼

    洛谷 U87561 魔法月饼 洛谷传送门 题目背景 \(9102\)年的中秋节注定与往年不同...因为在\(9102\)年的中秋节前夕,\(Seaway\)被告知今年的中秋节要新出一款月饼--魔法月饼 ...

  9. web框架--tornado之cookie与session初识

    cookie的本质其实就是在浏览器端保存的键值对, 每当浏览器端发送一次请求, 都会将这些键值对附加在请求中并发送给服务器端. 一.目录结构 二.main_pro.py #!/usr/bin/env ...

  10. Sentinel: 分布式系统的流量防卫兵

    前言 在 Spring Cloud 体系中,熔断降级我们会使用 Hystrix 框架,限流通常会在 Zuul 中进行处理,Zuul 中没有自带限流的功能,我们可以自己做限流或者集成第三方开源的限流框架 ...