前言

本文描述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. requests---session简介

    http协议是无状态的,也就是每个请求都是独立的.那么登录后的一系列动作,都需要用cookie来验证身份是否是登录状态,为了高效的管理会话,保持会话,于是就有了session session简介 se ...

  2. 团队项目-Alpha2版本发布

    第五次团队作业 序言 所属课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign 作业要求 https://w ...

  3. Android TextField : set focus + soft input programmatically

    Good sir, try this: edittext.setFocusableInTouchMode(true); edittext.requestFocus(); Im not sure, bu ...

  4. USACO Building Roads

    洛谷 P2872 [USACO07DEC]道路建设Building Roads 洛谷传送门 JDOJ 2546: USACO 2007 Dec Silver 2.Building Roads JDOJ ...

  5. Linux性能优化实战学习笔记:第二十三讲

    一.索引节点和目录 1.索引节点 2.目录项 3.关系 为了帮助你理解目录项.索引节点以及文件数据的关系,我画了一张示意图,你可以对照这张图,来回忆刚刚讲过的内容,把只知识和细节串联起来 4.Slab ...

  6. [LeetCode] 913. Cat and Mouse 猫和老鼠

    A game on an undirected graph is played by two players, Mouse and Cat, who alternate turns. The grap ...

  7. oracle--BBED (dump 深入实践三)

    一,工具介绍 bbed是Block Browser and Editor(块浏览编辑器)的缩写,它是Oracle数据库在安装时一起附带的工具. 一般此工具倾向于仅作为Oracle内部使用,且Oracl ...

  8. ROS-RouterOS KVM 安装 OpenWrt 旁路使用

    原文: http://bbs.routerclub.com/thread-104864-1-1.html 这里所讲是X86架构的RouteROS的KVM虚拟机,其实RouterOS的KVM很早就有,大 ...

  9. POI打印Excel

    一.POI概述 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 结构:HSSF - 提供读写Mic ...

  10. 2018-2019-2 20162329 《网络对抗技术》Exp7: 网络欺诈防范

    目录 Exp7: 网络欺诈防范 一. 基础问题回答 1. 通常在什么场景下容易受到DNS spoof攻击 二. 实验过程 1. 简单应用SET工具建立冒名网站 2. ettercap DNS spoo ...