当前位置:首页 » 《关注互联网》 » 正文

线程的六种状态

23 人参与  2024年09月14日 10:42  分类 : 《关注互联网》  评论

点击全文阅读


优质博文:IT-BLOG-CN

线程的状态在Thread.State这个枚举类型中定义:共有6种状态,可以调用线程Thread种的getState()方法获取当前线程状态。

public enum State   {         /**         * 新建状态(New):         * 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码        */         NEW,                  /**         * 就绪状态(Runnable)        * 一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。        * 当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。        * 当start()方法返回后,线程就处于就绪状态。        */         RUNNABLE,         /**        * 运行状态(Running)        * 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.        */        RUNNING,       /**         * 阻塞状态(Blocked)        * 线程运行过程中,可能由于各种原因进入阻塞状态:        * 1>线程通过调用sleep方法进入睡眠状态;        * 2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;        * 3>线程试图得到一个锁,而该锁正被其他线程持有;        * 4>线程在等待某个触发条件;        * ......        *  所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。        */         BLOCKED,              /**         * 一个正在等待的线程的状态。也称之为等待状态。        * 造成线程等待的原因有三种,分别是调用Object.wait()、join()以及LockSupport.park()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。        * 例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。        */         WAITING,                  /**         * 一个在限定时间内等待的线程的状态。也称之为限时等待状态。        * 造成线程限时等待状态的原因有五种,分别是:        *   Thread.sleep(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUntil(obj,long)。        */         TIMED_WAITING,           /**         * 一个完全运行完成的线程的状态。也称之为终止状态、结束状态。        */         TERMINATED;  }

一、六种状态转换图

操作系统中,线程被视为轻量级的进程,所以线程状态就是进程的状态,一个线程确实可以被视为一个轻量级进程Lightweight Process, LWP

二、新建状态(NEW)

new关键字新建一个线程,这个线程就处于新建状态。

每一个线程,在堆内存中都有一个对应的Thread对象。Thread t = new Thread();当刚刚在堆内存中创建Thread对象,还没有调用t.start()方法之前,线程就处在NEW状态。在这个状态上,线程与普通的java对象没有什么区别,就仅仅是一个堆内存中的对象。例如:

private void testStateNew() {    Thread thread = new Thread(() -> {});    System.out.println(thread.getState()); // 输出 NEW}

三、运行状态(RUNNABLE)

操作系统中的READY(就绪状态)和RUNNING(运行状态)两种状态,在Java中统称为RUNNABLE

READY(就绪状态)

当线程对象调用了start()方法之后,线程处于就绪状态,就绪意味着该线程可以执行,但具体啥时候执行将取决于JVM里线程调度器的调度。

【1】不允许对一个线程多次使用start:通过下面源码分析得知,在start()内部,有一个threadStatus变量。如果它不等于0,调用start()会直接抛出异常。

// 使用synchronized关键字保证这个方法是线程安全的public synchronized void start() {    // threadStatus != 0 表示这个线程已经被启动过或已经结束了    // 如果试图再次启动这个线程,就会抛出IllegalThreadStateException异常    if (threadStatus != 0)        throw new IllegalThreadStateException();    // 将这个线程添加到当前线程的线程组中    group.add(this);    // 声明一个变量,用于记录线程是否启动成功    boolean started = false;    try {        // 使用native方法启动这个线程        start0();        // 如果没有抛出异常,那么started被设为true,表示线程启动成功        started = true;    } finally {        // 在finally语句块中,无论try语句块中的代码是否抛出异常,都会执行        try {            // 如果线程没有启动成功,就从线程组中移除这个线程            if (!started) {                group.threadStartFailed(this);            }        } catch (Throwable ignore) {            // 如果在移除线程的过程中发生了异常,我们选择忽略这个异常        }    }}

我们在看下ThreadStatus的取值逻辑:如下源码可知,调用start()之后线程的状态就!0了。

// Thread.getState方法源码:public State getState() {    // get current thread state    return sun.misc.VM.toThreadState(threadStatus);}// sun.misc.VM 源码:// 如果线程的状态值和4做位与操作结果不为0,线程处于RUNNABLE状态。// 如果线程的状态值和1024做位与操作结果不为0,线程处于BLOCKED状态。// 如果线程的状态值和16做位与操作结果不为0,线程处于WAITING状态。// 如果线程的状态值和32做位与操作结果不为0,线程处于TIMED_WAITING状态。// 如果线程的状态值和2做位与操作结果不为0,线程处于TERMINATED状态。// 最后,如果线程的状态值和1做位与操作结果为0,线程处于NEW状态,否则线程处于RUNNABLE状态。public static State toThreadState(int var0) {    if ((var0 & 4) != 0) {        return State.RUNNABLE;    } else if ((var0 & 1024) != 0) {        return State.BLOCKED;    } else if ((var0 & 16) != 0) {        return State.WAITING;    } else if ((var0 & 32) != 0) {        return State.TIMED_WAITING;    } else if ((var0 & 2) != 0) {        return State.TERMINATED;    } else {        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;    }}

【2】其他状态转换为就绪状态:
  ☑️ 线程调用start(),新建状态转化为就绪状态。
  ☑️ 线程sleep(long)时间到,等待状态转化为就绪状态。
  ☑️ 阻塞式IO操作结果返回,线程变为就绪状态。
  ☑️ 其他线程调用join()方法,结束之后转化为就绪状态。
  ☑️ 线程对象拿到对象锁之后,也会进入就绪状态。

RUNNING(运行状态)

处于就绪状态的线程获得了CPU之后,真正开始执行run()方法的线程执行体时,意味着该线程就已经处于运行状态。需要注意的是,对于单处理器,一个时刻只能有一个线程处于运行状态。对于抢占式策略的系统来说,系统会给每个线程一小段时间处理各自的任务。时间用完之后,系统负责夺回线程占用的资源。下一段时间里,系统会根据一定规则,再次进行调度。

运行状态转变为就绪状态的情形:
  ☑️ 线程失去处理器资源。线程不一定完整执行的,执行到一半,说不定就被别的线程抢走了。
  ☑️ 调用yield()静态方法,提示调度程序,当前线程愿意放弃当前对处理器的使用。这时,当前线程将会被置为就绪状态,和其他线程一样等待调度,这时候根据不同优先级决定的概率,当前线程完全有可能再次抢到处理器资源。

如果线程长时间停留在在这个状态就不正常了,这说明线程运行的时间很长(存在性能问题),或者是线程一直得不得执行的机会(存在线程饥饿的问题)。

四、阻塞状态(BLOCKED)

线程正在等待获取java对象的监视器(也叫内置锁),即线程正在等待进入由synchronized保护的方法或者代码块。synchronized用来保证原子性,任意时刻最多只能由一个线程进入该临界区域,其他线程只能排队等待。

线程取得锁,就会从阻塞状态转变为就绪状态。

五、等待状态(WAITING)

进入该状态表示当前线程需要等待其他线程做出一些的特定的动作(通知或中断)。也就是说,如果不发生特定的事件,那么处在该状态的线程一直等待,不能获取执行的机会。比如:A线程调用了obj对象的obj.wait()方法,如果没有线程调用obj.notifyobj.notifyAll,那么A线程就没有办法恢复运行; 如果A线程调用了LockSupport.park(),没有别的线程调用LockSupport.unpark(A),那么A没有办法恢复运行。

六、超时等待状态(TIMED_WAITING)

区别于WAITING,它可以在指定的时间自行返回。

J.U.C中很多与线程相关类,都提供了限时版本和不限时版本的APITIMED_WAITING意味着线程调用了限时版本的API,正在等待时间流逝。当等待时间过去后,线程一样可以恢复运行。如果线程进入了WAITING状态,一定要特定的事件发生才能恢复运行;而处在TIMED_WAITING的线程,如果特定的事件发生或者是时间流逝完毕,都会恢复运行。

运行状态转换为超时等待:
  ☑️ 调用静态方法Thread.sleep(long)
  ☑️ 线程对象调用wait(long)方法
  ☑️ 其他线程调用指定时间的join(long)
  ☑️ LockSupport.parkNanos()
  ☑️ LockSupport.parkUntil()

七、消亡状态

线程执行完毕run方法或call方法,或者抛出没有捕获的ExceptionError异常而结束,线程都会停留在这个状态。这个时候线程只剩下Thread对象了,没有什么用了。

即线程的终止,表示线程已经执行完毕。前面已经说了,已经消亡的线程不能通过start再次唤醒。

需要注意的是:主线成和子线程互不影响,子线程并不会因为主线程结束就结束。


点击全文阅读


本文链接:http://zhangshiyu.com/post/159664.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1