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;}@Overridepublic 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
{@Overridepublic 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;}@Overridepublic 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()方法。
    @Overridepublic 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
{@Overridepublic 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;}@Overridepublic 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
{@Overridepublic 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;}@Overridepublic 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
{@Overridepublic void run(){System.out.println("进入t2线程");//让线程进入I/OSystem.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

转载于:https://www.cnblogs.com/GooPolaris/p/8079490.html

Java中一个线程只有六个状态。至于阻塞、可运行、挂起状态都是人们为了便于理解,自己加上去的。...相关推荐

  1. java中一个线程最小优先数_Java线程的优先级

    Java线程可以有优先级的设定,高优先级的线程比低优先级的线程有更高的几率得到执行(不完全正确,请参考下面的"线程优先级的问题"). 记住当线程的优先级没有指定时,所有线程都携带普 ...

  2. java中线程的生命周期_Java中的线程生命周期– Java中的线程状态

    java中线程的生命周期 Understanding Thread Life Cycle in Java and Thread States are very important when you a ...

  3. Java中的线程状态

    参考:https://my.oschina.net/goldenshaw?tab=newest&catalogId=3277710 1 线程状态 Java中的线程一共有6种状态. 在某个时刻, ...

  4. Java多线程编程(1)--Java中的线程

    一.程序.进程和线程   程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...

  5. Java中的线程基础篇-线程基本概念

    线程的概念.创建方式.生命周期.优缺点 一.基础知识 1. 进程.线程.协程 1.1 进程 1.2 线程 1.3 协程 2. 串行.并发.并行 2.1 串行 2.2 并发 2.3 并行 二.线程的创建 ...

  6. 不允许使用java方式启动_细品 Java 中启动线程的正确和错误方式

    细品 Java 中启动线程的正确和错误方式 前文回顾详细分析 Java 中实现多线程的方法有几种?(从本质上出发) start 方法和 run 方法的比较 代码演示:/** * * start() 和 ...

  7. java new一个线程执行完后会自动销毁吗_Java基础总结,超级全的面试题

    1. static关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?是否可以在 static 环境中访问非static 变量? stat ...

  8. 万字图文 | 学会Java中的线程池,这一篇也许就够了!

    来源:一枝花算不算浪漫 线程池原理思维导图.png 前言 Java中的线程池已经不是什么神秘的技术了,相信在看的读者在项目中也都有使用过.关于线程池的文章也是数不胜数,我们站在巨人的肩膀上来再次梳理一 ...

  9. 第五章 如何使用java中的线程打印偶数和奇数

    你有两个线程.您需要使用一个线程打印奇数,使用另一个线程打印偶数.您需要以自然顺序打印最多 MAX. 例如: 如果 MAX 为 10,则需要打印: 1 2 3 4 5 6 7 8 9 10 所以 1 ...

最新文章

  1. Configure,Makefile.am, Makefile.in, Makefile文件之间关系
  2. 使用vagrant基于官方的box制作自己的基础box
  3. visual studio 2010 professional , premium, ultimate各版本功能对比
  4. Windows IIS配置Jsp和php环境方法
  5. 软件测试 图覆盖,软件测试(四)——图覆盖
  6. 1088 Rational Arithmetic (20 分)【难度: 简单 / 知识点: 模拟】
  7. YYCache深入学习
  8. 单相桥式相控整流电路multisim仿真_单相半波整流 全波整流 桥式整流
  9. 联想台式机网卡驱动_【装机帮扶站】第339期:联想刃7000:是否还有选购价值?4000价位装机推荐...
  10. HYSBZ - 1050(旅行comf 并查集Java实现)
  11. datatype未定义是什么意思_TypeError:无法读取未定义的属性'then'
  12. 响应式滑动菜单_如何创建响应式滑动菜单
  13. 电商指标详细介绍和推荐系统常用评估指标
  14. CSDN10大博客栏目火热评选中
  15. OC算法练习-Hash算法
  16. 拦截导弹问题(Noip1999)
  17. 实现迭代服务器端和客户端
  18. 支持iphone的打印服务器,MAC苹果电脑系统 如何添加网络打印机(适用于Mac OS)
  19. VMware ESXi安装mac os
  20. 得一微YS9083XT量产工具及方法FW190402

热门文章

  1. 网页打印物理大小尺寸的单位换算
  2. [原创]java WEB学习笔记36:Java Bean 概述,及在JSP 中的使用,原理
  3. POJ3728 THE MERCHANT LCA RMQ DP
  4. Swift-EasingAnimation
  5. js中的cookie
  6. _java5条件阻塞Condition的应用
  7. Algorithm: 匈牙利算法
  8. 逆向生成的Dimac.JMail工程及测试项目
  9. redis集群linux安装教程,linux下redis集群的原生安装方式部署
  10. visio 模具_Visio2013 自定义模具 简单公式