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,并在其中添加线程状态的校验,从而得到定制化的线程同步工具。