Java中一个线程只有六个状态。至于阻塞、可运行、挂起状态都是人们为了便于理解,自己加上去的。
java中,线程的状态使用一个枚举类型来描述的。这个枚举一共有6个值: NEW(新建)、RUNNABLE(运行)、BLOCKED(锁池)、TIMED_WAITING(定时等待)、WAITING(等待)、TERMINATED(终止、结束)。
但是我发现大多数人的理解和上面的这六种还是有些差别,通常会加上阻塞状态,可运行状态,挂起状态。
这是Thread类描述线程状态的枚举类的源代码:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
一、大多数人对线程状态以及状态转换的理解
线程的状态转换:
当一个线程创建以后,就处于新建状态。那什么时候这个状态会改变呢?只要它调用的start()方法,线程就进入了锁池状态。
进入锁池以后就会参与锁的竞争,当它获得锁以后还不能马上运行,因为一个单核CPU在某一时刻,只能执行一个线程,所以他需要操作系统分配给它时间片,才能执行。所以人们通常把一个线程在获得锁后,获得系统时间片之前的状态称之为可运行状态,但Java源代码里面并没有可运行状态这一说。
当一个持有对象锁的线程获得CPU时间片以后,开始执行这个线程,此时叫做运行状态。
当一个线程正常执行完,那么就进入终止(死亡)状态。系统就会回收这个线程占用的资源。
但是,线程的执行并不是那么顺利的。一个正在运行的线程有可能会进入I/O交互,还可能调用sleep()方法,还有可能在当前线程当中有其它线程调用了join()方法。这时候线程就进入了阻塞状态(但这也只是人们在理解的时候意淫加上去的,源代码里也没有定义这一个状态)。阻塞状态的线程是没有释放对象锁的。当I/O交互完成,或sleep()方法完成,或其它调用join()方法的线程执行完毕。阻塞状态的线程就会恢复到可运行状态,此时如果再次获得CPU时间片就会进入运行状态。
一个处于运行状态的线程还可能调用wait()方法、或者带时间参数的wait(long milli)方法。这时候线程就会将对象锁释放,进入等待队列里面(如果是调用wait()方法则进入等待状态,如果是调用带时间参数的则进入定时等待状态)。
一个线程如果的调用了带时间参数的wait(long milli)方法进入了定时等待状态,那么只要时间一到就会进入锁池状态,并不需要notify()或notifyAll()方法来唤醒它。如果调用的是不带时间参数的wait()则需要notify()或notifyAll()这两个方法来唤醒它然后进入锁池状态。进入锁池状态以后继续参与锁的竞争。
当一个处于运行状态的线程调用了suspend()方法以后,它就会进入挂起状态(这一方法已经过时不建议使用)。挂起状态的线程也没有释放对象锁,它需要调用resume()方法以后才能恢复到可运行状态。将线程挂起容易导致程序死锁。
下面是我自己画线程状态转换图:

这是大多数对一个线程的状态的理解。我以前也是这么理解的,但是,现在我对线程的状态有了新的理解。这与大多数人的理解有些不一样。
二、我对线程状态以及转换的理解
下面是我对线程的自己的理解,如果有不对的地方希望懂的人可以指出来一起讨论一下。
据官方源码,一个线程有六个状态,没有阻塞状态,没有可运行,没有挂起状态。
所以,现在我要提出一个观点:一个处于等待状态、定时等待状态的线程它也可能持有对象锁。例如调用sleep(long millis)会使线程进入等待状态,但是没有释放锁。
线程没有阻塞状态,那一个正在运行的线程进入I/O,或调用sleep()方法,或当前线程当中有其它线程调用了join()方法时,线程就会进入什么状态呢,它总得有六个当中的一个吧。
①、当线程调用sleep()方法或当前线程中有其他线程调用了带时间参数的join()方法的时候进入了定时等待状态(TIMED_WAITING)。

②、当其他线程调用了不带时间参数的join()方法时进入等待状态(WAITING)。

③、当线程遇到I/O的时候还是运行状态(RUNNABLE)。
④、当一个线程调用了suspend()方法挂起的时候它还是运行状态(RUNNABLE)。
现在我要来证明一下以上四点,如果证明过程有误,希望能够得到指正。这些代码的可以直接复制来运行一下。
证明一:当线程调用sleep()方法的时候进入了定时等待状态。
现在两个线程t1、t2,t1持有t2的一个引用。启动两个线程,t2启动后立即睡眠,让t1打印t2的状态。这样就可以看到睡眠时候的线程是六个当中的哪一个状态了。
public class Test1
{
public static void main(String[] args)
{
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.setThread2(t2); t1.start();
t2.start();
}
} //Thread1负责打印两个线程的状态。
class Thread1 extends Thread
{
private Thread2 t2; public void setThread2(Thread2 t2)
{
this.t2 = t2;
} @Override
public void run()
{
System.out.println("进入t1线程");
for(int i = 0; i < 5; i++)
{
try
{
System.out.println("t1 的状态: " + getState());
System.out.println("t2 的状态: " + t2.getState());
System.out.println(); //为了减少打印次数,所以t1每打印一次睡1秒
Thread.sleep(1000);
} catch (InterruptedException e)
{ }
}
}
} class Thread2 extends Thread
{
@Override
public void run()
{
System.out.println("进入t2线程,马上进入睡眠");
try
{
//睡眠5秒钟。
sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("t2睡眠结束");
}
}
上面的程序执行打印的结果

说明调用sleep()方法以后线程处于定时等待状态(TIMED_WAITING)。至于网上一直说的处于等待状态的线程不持有对象锁这种说法,我不知道这是官方给出的还是人们自己定义的。
证明二:当其他线程调用了join()方法时进入等待状态。
现在有三个线程t1 、t2 、t3。t1负责打印三个线程的状态。t2线程持有t3线程的引用,当进入t2线程以后,立即启动t3线程,并调用t3.join()方法。当t3加入后t2就是等待状态。
public class Test1
{
public static void main(String[] args)
{
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
Thread3 t3 = new Thread3(); //t1需要持有t2,t3的引用,以便打印他们的状态。
t1.setThread2(t2,t3); //t2需要持有t3的引用,以便t3能够在t2执行时加入(调用join()方法)
t2.setTh3(t3); t1.start();
t2.start();
}
} //Thread1负责打印所有线程的状态。
class Thread1 extends Thread
{
private Thread2 t2;
private Thread3 t3; public void setThread2(Thread2 t2, Thread3 t3)
{
this.t2 = t2;
this.t3 = t3;
} @Override
public void run()
{
System.out.println("进入t1线程");
for(int i = 0; i < 5; i++)
{
try
{
System.out.println("t1 的状态: " + getState());
System.out.println("t2 的状态: " + t2.getState());
System.out.println("t3 的状态: " + t3.getState());
System.out.println(); //为了减少打印次数,所以t1每打印一次睡1秒
Thread.sleep(1000);
} catch (InterruptedException e)
{ }
}
}
} class Thread2 extends Thread
{
private Thread3 t3; public void setTh3(Thread3 t3)
{
this.t3 = t3;
} //当进入t2线程以后马上启动t3线程并调用join()方法。
@Override
public void run()
{
System.out.println("进入t2线程,t3准备加入(调用join()方法)");
t3.start();
try
{
t3.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("t2执行结束");
}
}
class Thread3 extends Thread
{
@Override
public void run()
{
System.out.println("进入t3线程,准备睡眠"); //本来是想让t3线程做加法运算的,奈何电脑算太快了,所以改为睡眠。因为睡眠不释放锁,所以效果一样。
try
{
sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
} System.out.println("t3线程结束");
}
}
这是运行的打印结果,当t3加入后t2处于等待状态

根据结果看,说明当一个正在执行的线程在其他线程调用join()方法以后进入了等待状态。
证明三:当一个线程调用了suspend()方法的时候它还是运行状态(RUNNABLE)。
package com.zcd.observe; public class Test1
{
public static void main(String[] args)
{
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2(); //t1需要持有t2,以便打印状态,和控制它恢复运行。
t1.setThread2(t2); t1.start();
t2.start();
}
} //Thread1负责打印所有线程的状态。
class Thread1 extends Thread
{
private Thread2 t2; public void setThread2(Thread2 t2)
{
this.t2 = t2;
} @Override
public void run()
{
System.out.println("进入t1线程");
for(int i = 0; i < 6; i++)
{
try
{
System.out.println("t1 的状态: " + getState());
System.out.println("t2 的状态: " + t2.getState());
System.out.println(); if(i == 3)
{
//恢复t2的运行。
t2.resume();
} //为了减少打印次数,所以t1每打印一次睡1秒
Thread.sleep(1000);
} catch (InterruptedException e)
{ }
} }
} class Thread2 extends Thread
{
@Override
public void run()
{
System.out.println("进入t2线程,挂起");
//将线程挂起。让t1来控制它的恢复运行。
suspend(); System.out.println("t2已经恢复运行");
System.out.println("t2正在打印1");
System.out.println("t2正在打印2");
System.out.println("t2正在打印3"); System.out.println("t2线程结束");
}
}
执行结果截图

说明:当一个线程调用了suspend()方法的时候它还是运行状态(RUNNABLE)。
证明四:当线程遇到I/O的时候还是运行状态。
package com.zcd.observe; import java.util.Scanner; public class Test1
{
public static void main(String[] args)
{
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2(); t1.setThread2(t2); t1.start();
t2.start();
}
} //Thread1负责打印所有线程的状态。
class Thread1 extends Thread
{
private Thread2 t2; public void setThread2(Thread2 t2)
{
this.t2 = t2;
} @Override
public void run()
{
System.out.println("进入t1线程");
for(int i = 0; i < 6; i++)
{
try
{
System.out.println("t1 的状态: " + getState());
System.out.println("t2 的状态: " + t2.getState());
System.out.println(); //为了减少打印次数,所以t1每打印一次睡1秒
Thread.sleep(1000);
} catch (InterruptedException e)
{ }
}
System.out.println("进入t1线程结束");
}
} class Thread2 extends Thread
{
@Override
public void run()
{
System.out.println("进入t2线程"); //让线程进入I/O
System.out.println("请输入数据:");
Scanner scan = new Scanner(System.in);
String read = scan.nextLine();
System.out.println("您输入的数据为:"+read); System.out.println("t2线程结束");
}
}
执行结果截图:

说明:当线程遇到I/O的时候还是运行状态。
下面在附上我画的状态转换图,一共只有六个状态。

我觉得以上两种理解方式只是站在同的角度去理解而已。
本文为作者原创,转载需要声明并且附上本文地址:http://www.cnblogs.com/GooPolaris/p/8079490.html
Java中一个线程只有六个状态。至于阻塞、可运行、挂起状态都是人们为了便于理解,自己加上去的。的更多相关文章
- 浅析Java中的线程池
Java中的线程池 几乎所有需要异步或并发执行任务的程序都可以使用线程池,开发过程中合理使用线程池能够带来以下三个好处: 降低资源消耗 提高响应速度 提高线程的可管理性 1. 线程池的实现原理 当我们 ...
- 【Java中的线程】java.lang.Thread 类分析
进程和线程 联想一下现实生活中的例子--烧开水,烧开水时是不是不需要在旁边守着,交给热水机完成,烧开水这段时间可以去干一点其他的事情,例如将衣服丢到洗衣机中洗衣服.这样开水烧完,衣服洗的也差不多了.这 ...
- [译]线程生命周期-理解Java中的线程状态
线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...
- Java中的线程状态转换和线程控制常用方法
Java 中的线程状态转换: [注]:不是 start 之后就立刻开始执行, 只是就绪了(CPU 可能正在运行其他的线程). [注]:只有被 CPU 调度之后,线程才开始执行, 当 CPU 分配给你的 ...
- Java多线程编程(1)--Java中的线程
一.程序.进程和线程 程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...
- Java中的线程到底有哪些安全策略
摘要:Java中的线程到底有哪些安全策略呢?本文就为你彻底分析下! 本文分享自华为云社区<[高并发]线程安全策略>,作者:冰 河 . 一.不可变对象 不可变对象需要满足的条件 (1)对象创 ...
- Java中的线程
http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...
- Java中的线程Thread总结
首先来看一张图,下面这张图很清晰的说明了线程的状态与Thread中的各个方法之间的关系,很经典的! 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口. 要注意的是Threa ...
- 第9章 Java中的线程池 第10章 Exector框架
与新建线程池相比线程池的优点 线程池的分类 ThreadPoolExector参数.执行过程.存储方式 阻塞队列 拒绝策略 10.1 Exector框架简介 10.1.1 Executor框架的两级调 ...
随机推荐
- Linux(CentOS6)系统安装Windows字体
这里说个插曲,最近在linux(CentOS6)服务器上面发布了个Web项目,结果发现,有一处的汉字字体渲染不出来,显示的是“□□□□”.因为我的tomcat编码已经修改了URIEncoding为UT ...
- PHP正则表达式修饰符的种类及介绍
◆i :如果在修饰符中加上"i",则正则将会取消大小写敏感性,即"a"和"A" 是一样的.◆m:默认的正则开始"^"和结 ...
- Linux 安装 JDK
本篇博客用于记录一下在 Linux 系统下安装 Java 环境. 在大部分的 Linux 系统中都有安装 Open JDK,所以最好是先卸载 Open JDK 后在进行我们的 JDK 安装.Open ...
- python字典操作和内置方法
一 字典基本介绍 python中只有字典是映射结构,通过key取值,并且key是不可变数据类型,而value可以是任意数据类型. 字典通过一个花括号,里面存放key:value的数据结构来定义.理论上 ...
- [转]Material使用08 MdDialogModule、MdAutocompleteModule
本文转自:https://www.cnblogs.com/NeverCtrl-C/p/8125346.html 1 MatDialog 1.1 简要描述 MdDialog是一个服务,可以利用它来打开一 ...
- [转]微擎应用笔记3--manifest.xml文件使用说明
本文转自:https://blog.csdn.net/seven_north/article/details/79508121 微擎在安装或卸载模块时会根据manifest.xml生成(或删除)数据库 ...
- canvas处理压缩照片并回显:https://cengjingdeshuige.oss-cn-beijing.aliyuncs.com/20180512/cannovs%E5%AD%A6%E4%B9%A0.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue项目导入外部css样式和js文件
<template> <div id="app" > </div> </template> <script src=" ...
- Ubuntu 16.04 LTS 下安装 ibus-rime 输入法
搜 Linux 下粤拼输入法的时候发现了 Rime,由于 fcitx 下的拼音输入体验实在不太好(搜狗是在我的电脑上完全坏掉了,调不出来,配置文件的问题一直没解决:谷歌是好过没有),于是安装 ibus ...
- CSS水平居中的三种方法
CSS中经常会用到元素居中,那么今天我为大家分享几种水平居中的方法,下面代码都可以达到同样的居中效果,来不及解释了,快上马(码): 一.margin : 0 auto; <head> &l ...