图解JAVA线程的几个状态(JAVA笔记-线程基础篇)

Java中线程的状态,是线程在生命周期中不同时间段的状态。举个例子,我们拿小白做作业的例子比作是一条线程要执行的任务。小白掏出作业还没有开始写作业,这就说明线程准备好了。小白开始动笔写了,他在写作业了,他在奋笔疾书的写作业了,这说明线程在运行状态。小白的弟弟小黑把他笔抢去捅蚂蚁洞了,现在小白没法做作业了(他怎么就一个笔?剧情需要....),现在这条线程阻塞状态了也可能是等待状态。小白把小黑揍了一顿,抢来笔继续写作业,现在是线程又是运行状态了。几经波折,小白终于做完了作业了,线程结束。

Java中线程有几种状态

既然谈到线程,怎么离得开Thread这个类。Java中的几个状态也都写在这个类的源码里面了。
Thread这个类中有一个内部枚举类型。看看源码。

public class Thread implements Runnable {
    //省略n多源码...详情看源码

    /**
     * A thread state.  A thread can be in one of the following states:
     * 线程状态。一个线程的状态就下面这些了。
     *
     * 省略n多注释...详情看源码
     */
    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

    //省略n多源码...详情看源码
}

一共6种状态。但是,如果你看过很多大佬画的图,你会发现,他们画的图中,会还有一种是RUNNING的状态?(不信你去百度)但是这个JAVA只有一个RUNNABLE。怎么回事呢?请往下看。

Runnable和Running状态的区别、

为什么大部分JAVA线程状态图里都有RUNNING状态,但是JAVA源码里没有这个状态呢??

因为JAVA没法用代码把线程变成RUNNING状态,他只能让线程是准备好运行的状态RUNNABLE

  • 不正经举例:
    就像妃子们没法控制自己今晚会不会被翻牌(代表RUNNING状态),她们只能准备好被翻牌,也就是RUNNABLE状态。其实CPU也不是同步运行所有线程的,每次它只能运行一个线程,就像每次皇上只能翻一个妃子的牌一样。

CPU只会不断的切换运行的线程,如果切换的足够快,看上去就像是两个线程在同步运行。
(你可以想象一个反复横跳的人,只要他跳的足够快,你看上去也就是有两个人了,好像是影分身。但其实还是只有一个人,不断的挑来跳去罢了,就像CPU不断的切换运行的线程一样。)

  • 结论
    running状态对于线程来说是存在的。但是java控制不了,只能把线程变成runable状态。所以源码中也就没有running了。
    实在不懂,学习的时候可以把runable和running看成一个状态就好了。或者就看成7种状态

  • 注意
    虽然java无法将runable状态变成runing状态,但是他可以把running状态变成runable状态。。。。。。。。。通过yield();方法。

JAVA线程状态图

下面是要给简单的图,表示这个线程运行的很顺利,走的也很流畅。没有什么复杂的操作。没有什么很复杂的箭头。

下面来个复杂一点点的图

说明

序号 方法
thread.start()
代码(方法/代码块)被synchronized修饰,线程等待获取锁。
线程获取了锁。
.yield();
.sleep(long);
.wait(long);
.join(long);
LockSupport.parkNanos(long);
LockSupport.parkUntil();
.notify();
.notifyAll();
LockSupport.unpark(thread);
.wait();
.join();
LockSupport.park();
.notify();
.notifyAll();
LockSupport.unpark(thread);
线程里的代码运行结束
线程抛出一个没有捕获的异常或者error

线程各个状态说明和代码实例

  • NEW
    新建状态新建线程对象。

      public static void main(String[] args) {
    
          Thread thread=new Thread(new Runnable() {
              public void run() {
                  System.out.println("我是一个新建的线程");
              }
          });
          //通过.getState();可以获取线程状态
          System.out.println(thread.getState());
      }
    

    输出:NEW

  • RUNNABLE
    运行.start()将线程变成就绪状态,随时准备着被cpu翻牌。

    public static void main(String[] args) {
    
          Thread thread=new Thread(new Runnable() {
              public void run() {
                  System.out.println("我是一个新建的线程");
              }
          });
          //start线程
          thread.start();
          System.out.println(thread.getState());
      }
    

    输出:
    RUNNABLE
    我是一个新建的线程

  • BLOCKED
    线程在等待其他线程把公用的锁对象释放,所以他就一直blocked。就像小白一直等着他弟弟把笔还给他一样。

    自定义一个线程类,运行run()方法中要有同步代码块。然后写一个死循环,让代码一直运行,也就一直不释放锁对象。

      public class MyThread extends Thread {
          private Object clock;
          //锁对象
          public MyThread(Object clock){
              this.clock=clock;
          }
          @Override
          public void run() {
              synchronized (clock){
                  //让这个线程一直运行,所以他就不会释放clock锁给其他线程用
                  while (true){}
              }
          }
      }
    

    测试代码

      public static void main(String[] args)throws Exception {
          Object clock=new Object();
          //两个线程使用同一个锁对象
          MyThread myThread1=new MyThread(clock);
          MyThread myThread2=new MyThread(clock);
          //启动两个线程
          myThread1.start();
          myThread2.start();
          //为了不断输出线程的状态,我们在主线程里也写要给死循环。这样就能不断输出线程的状态了。
          while (true){
              System.out.println("myThread1状态是:"+myThread1.getState());
              System.out.println("myThread2状态是:"+myThread2.getState());
              //没过3秒输出一次
              Thread.sleep(3*1000);
          }
      }
    

    输出
    myThread1状态是:RUNNABLE
    myThread2状态是:BLOCKED
    myThread1状态是:RUNNABLE
    myThread2状态是:BLOCKED
    myThread1状态是:RUNNABLE
    myThread2状态是:BLOCKED
    myThread1状态是:RUNNABLE
    myThread2状态是:BLOCKED
    .......

  • WAITING
    线程进入等待状态,只有其他线程调用⑧中的方法,才会重新进入RUNNABLE状态。

      public static void main(String[] args)throws Exception {
    
          Thread thread=new Thread(new Runnable() {
              public void run() {
                  LockSupport.park();
              }
          });
          thread.start();
          //同样的套路,把主线程循环,每3秒输出一下线程的状态
          while (true){
              System.out.println(thread.getState());
              Thread.sleep(3*1000);
          }
    
      }
    

    输出
    RUNNABLE
    WAITING
    WAITING
    WAITING
    ....

    第一个是因为运行太快了,thread线程还没来得及运行完LockSupport.park();主线程就把他状态输出了。

  • TIMED_WAITING
    这个状态跟上面相似,不过这个状态不一定需要别的线程调用notifyAll()这些方法来重新启动线程,在规定的时间后,此状态的线程会自动变成RUNNABLE状态。
    注意是不一定需要
    将上面的LockSupport.park();改成LockSupport.parkUntil(10*1000);。意识是10秒后自动从TIMED_WAITING变成RUNNABLE

       while (true){
          LockSupport.parkNanos(30*1000);
      }
    

    输出
    RUNNABLE
    TIMED_WAITING
    TIMED_WAITING
    ....

  • TERMINATED
    就是线程运行结束了的状态。
    直接上代码

      public void run1() throws Exception{
          Thread thread=new Thread(new Runnable() {
              public void run() {
                  System.out.println("线程运行结束了");
              }
          });
          thread.start();
          //主线程休眠1秒,保证thread运行结束
          Thread.sleep(1000);
          System.out.println(thread.getState());
      }
    

    输出:
    线程运行结束了
    TERMINATED

作者:BobC

文章原创。如你发现错误,欢迎指正,在这里先谢过了。博主的所有的文章、笔记都会在优化并整理后发布在个人公众号上,如果我的笔记对你有一定的用处的话,欢迎关注一下,我会提供更多优质的笔记的。
原文地址:https://www.cnblogs.com/Eastry/p/13046617.html