线程的六种状态——从Java API层面出发

1 API源码

在API种 java.lang.Thread.State 枚举出来了六种线程状态,下面为截取代码。

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;
    }
线程状态导致状态发生条件
NEW (新建)线程刚被创建,但是并未启动,还没调用 start 方法,只有线程对象,没有线程特征
Runnable (可运行)线程可以在 java 虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器,调用了 t.start() 方法:就绪(经典叫法),包括操作系统层面的运行,可运行和阻塞状态
Blocked (锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态;当该线程持有锁时,该线程将变成 Runnable 状态。区分与操作系统方面的阻塞。
Waiting (无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态,进入这个状态后不能自动唤醒,必须等待另一个线程调用 notify 或者 notifyAll 方法才能唤醒
Timed Waiting (计时等待)有几个方法有超时参数,调用将进入 Timed Waiting 状态,这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait
Teminated (被终止)run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡

 2. 示意图和分析

 下面依次分析各个箭头的含义,假设现在已经创建好了一个线程t:

2.1 NEW-->RUNNABLE

当调用t.start()方法时,状态由NEW-->RUNNABLE。

2.2 RUNNABLE<--> WAITING (1)

当t线程调用synchronized(obj)获取了对象锁以后:

调用obj.wait()方法,t线程会从可运行态转换为无限等待状态

当调用obj.notify(),obj.notifyAll(),t.interrupr()时,会出现竞争锁成功和竞争锁失败两种情况。当竞争锁成功时,t线程会从无限等待状态切换为可运行状态;当竞争锁失败时,会从无限等待状态切换到可运行状态后再到阻塞状态。

2.3  RUNNABLE<--> WAITING (2)

当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING ,注意是当前线程在t 线程对象的监视器上等待。 t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE。

2.4 RUNNABLE<--> WAITING (3)

当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING ,调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING --> RUNNABLE。

当然有特殊情况,比方说先调用了unpark方法,那么park方法就不生效了。

2.5  RUNNABLE<-->TIMED_ WAITING (1)

当t线程获取到synchronized(obj)对象锁以后,调用obj.wait(long n)方法时,t线程会从RUNNABLE-->TIMED_ WAITING。

时间超过了n以后,或者调用 obj.notify() , obj.notifyAll() , t.interrupt()后,线程开始锁竞争。竞争的结果和2.2中类似,当竞争锁成功时,t线程会从计时等待状态切换为可运行状态;当竞争锁失败时,会从计时等待状态切换到可运行状态后再到阻塞状态。

2.6 RUNNABLE<-->TIMED_ WAITING (2)

当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING 。注意是当前线程在t 线程对象的监视器上等待,当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 TIMED_WAITING --> RUNNABLE。

2.7 RUNNABLE<-->TIMED_ WAITING (3)

当前线程调用 Thread.sleep(long n) ,当前线程从 RUNNABLE --> TIMED_WAITING,当前线程等待时间超过了 n 毫秒,当前线程从 TIMED_WAITING --> RUNNABLE。

2.8 RUNNABLE<-->TIMED_ WAITING (4)

当前线程调用 LockSupport.parkNanos(long nanos) LockSupport.parkUntil(long millis) 时,当前线 程从 RUNNABLE --> TIMED_WAITING。 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从 TIMED_WAITING--> RUNNABLE。

2.9 RUNNABLE<--> BLOCKED

t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED 。

持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上 BLOCKED 的线程重新竞争,如果其中 t 线程竞争 成功,从 BLOCKED --> RUNNABLE ,其它失败的线程仍然 BLOCKED。

2.10 RUNNABLE <-->TERMINATED

当前线程所有代码运行完毕,进入 TERMINATED。

一张图概括总结

 3. 与五种状态的联系

  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为 是可运行)
  • BLOCKED,WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分

4. 为什么需要线程状态?

 这个问题,可以从两个角度来说明:生命周期与资源管理

  • 一方面,线程状态很好地刻画了线程的整个生命周期,对生命周期中不同阶段进行了有效划分。

  • 另一方面,资源是有限的,需求是无限的。所以需要将系统资源有意识地进行调度,合理利用比较优势,追求帕累托最优。

实现后者的,就是利用线程在生命周期的不同阶段这一天然属性带来的状态刻画,进行的分组。CPU调度只需要关注运行状态的线程。而阻塞,等待等线程,都有着属于自己的一套处理方式。最终获得资源(开发时对复杂性的应对,运行时对系统资源对消耗,应用者心智模型的成长等)的优化分配。

 5. JDK中为什么需要定义Java线程状态?

前文有说到,Java中很少直接使用到线程状态。那么为什么还要在JDK中定义Java的六种线程状态呢?
一方面,通过信息透明的方式,降低使用者使用时建立心智模型的成本。如,现在的我们可以通过打印日志,打断点,快速了解Java的各个线程状态,并清楚了解产生的原因。从而大大提高了我们对Java线程的认识,进而更为愉快地拥抱JUC包下诸如线程池等工具。
另一方面,通过可以直接应用的状态枚举,我们可以很好地对现有工具进行二次开发等。如我们可以通过扩展AQS,并在其中添加线程状态的校验,从而得到定制化的线程同步工具。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值