zoukankan      html  css  js  c++  java
  • 图解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

    文章原创。如你发现错误,欢迎指正,在这里先谢过了。博主的所有的文章、笔记都会在优化并整理后发布在个人公众号上,如果我的笔记对你有一定的用处的话,欢迎关注一下,我会提供更多优质的笔记的。
  • 相关阅读:
    《手把手教你》系列技巧篇(三十二)-java+ selenium自动化测试-select 下拉框(详解教程)
    《手把手教你》系列技巧篇(三十一)-java+ selenium自动化测试- Actions的相关操作-番外篇(详解教程)
    《手把手教你》系列技巧篇(三十)-java+ selenium自动化测试- Actions的相关操作下篇(详解教程)
    《手把手教你》系列技巧篇(二十九)-java+ selenium自动化测试- Actions的相关操作上篇(详解教程)
    ApplicationContext在非Service类中调用Spring的Serivce类
    java多字段排序
    PropertyDescriptor动态setter和getter设置对象属性
    presto timestmp使用
    【效能提升】我们排查一个bug的过程是怎么样的?
    【效能提升】上线前漏了SQL脚本,漏加上某个配置项了?
  • 原文地址:https://www.cnblogs.com/Eastry/p/13046617.html
Copyright © 2011-2022 走看看