線程的狀態(tài)
- 在Java源碼中給線程定義了6種狀態(tài)
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
:線程剛被創(chuàng)建叽奥,還沒有調(diào)用start()
之前就處于該狀態(tài)RUNNABLE
:在調(diào)用start()
之后冲茸,線程就處于運行狀態(tài)通孽。Java線程中將就緒(ready)和運行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運行仲义,我們知道齐蔽,操作系統(tǒng)對線程的調(diào)度多是搶占式的产弹。也就是說調(diào)用了start()
之后不一定馬上可以執(zhí)行派歌,而是被放置在可運行線程池中,說明該線程是處于就緒狀態(tài),可以被運行了胶果,這個時候的線程在等待線程調(diào)度常挚,直到等到cpu時間分片到了才會真正的執(zhí)行。注釋中也說明了該問題:but it may be waiting for other resources from the operating system such as processor.
BLOCKED
:處于BLOCKED
表示該線程阻塞于鎖稽物。當線程在等待一個監(jiān)視器的鎖去進入一個synchronized block/method
(同步[塊|方法])或者在調(diào)用了Object#wait()
后被notify后重新進入synchronized block/method
依舊需要等待該鎖奄毡,線程就會處于阻塞狀態(tài)WAITING
:處于WAITING
表明該線程正在等待另一個線程執(zhí)行特定的操作。當調(diào)用了Object#wait()
贝或、Thread#join()
吼过、LockSupport#park()
中的某一個方法(需注意是with no timeout
的)之后。線程就會處于等待狀態(tài)咪奖。等待的是其它線程的通知或者終止狀態(tài)TIMED_WAITING
: 具有指定等待時間的等待線程的線程狀態(tài)盗忱。與WAITING
不同的是該狀態(tài)在指定等待時間之后會自行返回。當調(diào)用了Thread.sleep
羊赵、Object#wait(long)
趟佃、Thread.join(long)
、LockSupport#parkNanos
昧捷、LockSupport#parkUntil
方法之一后闲昭。線程就處于等待狀態(tài),在指定的時間沒有被喚醒或者等待線程沒有結(jié)束靡挥,會被系統(tǒng)自動喚醒序矩,正常退出TERMINATED
:終止狀態(tài),表明線程已經(jīng)執(zhí)行完了run方法跋破。該狀態(tài)只是代表著線程已經(jīng)執(zhí)行完了run方法簸淀,實際上操作系統(tǒng)里線程可能被注銷了,也可能復(fù)用給其它線程任務(wù)請求毒返。
狀態(tài)驗證
- 首先來看線程沒有任何阻塞租幕、等待處理的情況
class ThreadTest {
@Test
fun test() {
val thread = MyThread()
println("thread state before start:${thread.state}")
thread.start()
Thread.sleep(200) //睡200ms等待run執(zhí)行完成
println("thread state after run :${thread.state}")
}
class MyThread :Thread(){
override fun run() {
super.run()
println("thread state on run:$state")
}
}
}
- 運行結(jié)果如下
- 由于搶占鎖而處于阻塞狀態(tài)
class ThreadTest {
@Test
fun test() {
val lock = Object()
val thread1 = MyThread("thread 1",lock)
thread1.start()
Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行
val thread2 = MyThread("thread 2",lock)
thread2.start()
Thread.sleep(200) //等待200ms 讓thread2被調(diào)度執(zhí)行 ,此時thread1已經(jīng)持有鎖了
println("thread2 state :${thread2.state}")
}
class MyThread constructor(name: String,var lock:Object) : Thread(name) {
override fun run() {
super.run()
println("$name try to hole the lock")
synchronized(lock) {
println("$name hole the lock now")
sleep(1000 * 1000)
}
}
}
}
- 運行結(jié)果如下
- 由于等待其它線程的特定操作而處于等待狀態(tài),直到被通知
class ThreadTest {
@Test
fun test() {
val lock = Object()
val thread1 = MyThread("thread 1",lock)
thread1.start()
Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
println("thread1 state :${thread1.state}")
}
class MyThread constructor(name: String,var lock:Object) : Thread(name) {
override fun run() {
super.run()
synchronized(lock){ //在調(diào)用lock之前需要使用synchronized語義綁定住被wait/notify的對象
println("$name state:$state")
lock.wait()
println("$name state:$state")
}
}
}
}
- 運行結(jié)果如下
class ThreadTest {
@Test
fun test() {
val lock = Object()
val thread1 = MyThread("thread 1",lock)
thread1.start()
Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
println("thread1 state :${thread1.state}")
synchronized(lock) {
lock.notifyAll() //通知
}
Thread.sleep(200)
println("thread1 state after notify:${thread1.state}")
}
class MyThread constructor(name: String,var lock:Object) : Thread(name) {
override fun run() {
super.run()
synchronized(lock){ //在調(diào)用wait()之前需要使用synchronized語義綁定住被wait/notify的對象(獲取到鎖)
println("$name state:$state")
lock.wait()
println("$name state:$state")
}
}
}
}
- 運行結(jié)果如下
- 具有指定等待時間的等待線程的線程狀態(tài)
class ThreadTest {
@Test
fun test() {
val thread1 = MyThread("thread 1")
Object().wait(1000)
thread1.start()
Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
println("thread1 state :${thread1.state}")
Thread.sleep(1000) //等待1000ms 讓線程run執(zhí)行完成
println("thread1 state :${thread1.state}")
}
class MyThread constructor(name: String) : Thread(name) {
override fun run() {
super.run()
println("$name state:$state")
sleep(1000)
println("$name state:$state")
}
}
}
- 運行結(jié)果如下
-
Object#wait(long)
進入等待狀態(tài)
class ThreadTest {
@Test
fun test() {
val lock = Object()
val thread1 = MyThread("thread 1",lock,false)
thread1.start()
Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
println("thread1 state :${thread1.state}")
Thread.sleep(3000) //等待3000ms 讓線程thread1 run能夠繼續(xù)執(zhí)行
thread1.condition = true //讓條件滿足
Thread.sleep(2000) ///等待2000ms 確保讓線程thread1 run執(zhí)行
println("thread1 state :${thread1.state}")
}
class MyThread constructor(name: String,var lock:Object,var condition:Boolean) : Thread(name) {
override fun run() {
super.run()
synchronized(lock){
while (!condition){
println("$name state:$state")
lock.wait(1000)
println("$name continue run at ${System.currentTimeMillis()}")
}
println("do sth when meet the conditions")
println("$name state:$state")
}
}
}
}
- 運行結(jié)果如下
- 需要注意的是實際上每次
thread 1
由于while循環(huán)每次超時喚醒之后又會重新進入TIMED_WAITING
拧簸,然后才continue run
直到條件符合退出循環(huán) - 在
TIMED_WAITING
狀態(tài)之后是有一個最多等待時間的劲绪,在超時之后線程會自動被喚醒,無需手動notify
狡恬,當然也可以提前通過notify
喚醒線程珠叔。從上面的運行結(jié)果相信讀者可以很好的體會出WAITING
和TIMED_WAITING
的區(qū)別
狀態(tài)圖
- 通過源碼注釋及上面的分析我們可以畫一個狀態(tài)圖
Object.wait、Thread.sleep弟劲、LockSupport.park祷安、等方法的異同
在前面的狀態(tài)圖中列舉了非常多方法,那么它們的的區(qū)別是什么呢兔乞?怎么使用它們才能保證線程的狀態(tài)是符合我們的預(yù)期的(程序的功能符合預(yù)期)
-
Object.wait
- 調(diào)用之前需要先獲取鎖汇鞭,也就是被
synchronized
原語綁定 -
調(diào)用之后線程會釋放占有的鎖 凉唐,從測試用例的調(diào)用過程就可以看出來。調(diào)用
obj.wait
的時候和其它多線程調(diào)用obj.notifyAll
的時候都需要先獲取鎖霍骄,如果調(diào)用obj.wait
的時候沒有釋放鎖台囱,那么別的線程就無法獲取該對象鎖來notify
了 - 不傳時間會一直阻塞,直到另一個線程調(diào)用
notify()/notifyAll()
將其喚醒读整,傳入時間簿训,如果其它線程一直沒有調(diào)用notify()/notifyAll()
, 到了時間會自動喚醒米间。被喚醒之后如果立即獲得鎖强品,會繼續(xù)執(zhí)行,如果沒有獲得鎖屈糊,會進入同步隊列等待獲取鎖的榛,也就是不一定會執(zhí)行后面的邏輯 - 可響應(yīng)
interrupt()
中斷,并且拋出InterruptedException
-
notify()/notifyAll()
必須在wait()
之后調(diào)用,否則會拋出IllegalMonitorStateException
異常,并且無法指定喚醒哪個線程
- 調(diào)用之前需要先獲取鎖汇鞭,也就是被
-
Thread.join
- 從jdk源碼中可以看到該方法的實現(xiàn)的原理實際上利用了
synchronized(lock) {while (isAlive()) { lock.wait(0);}}
實現(xiàn)的逻锐,也就是說調(diào)用該線程的join方法的時候?qū)嶋H上需要先獲取該線程對象的鎖夫晌,否則也是會被阻塞的 - 線程A調(diào)用線程B#join方法之后,線程A一直阻塞直到B為TERMINATED狀態(tài)即被喚醒昧诱,所以一般可用來實現(xiàn)線程的同步運行晓淀,即按順序執(zhí)行
- 可響應(yīng)
interrupt()
中斷,并且拋出InterruptedException
- 從jdk源碼中可以看到該方法的實現(xiàn)的原理實際上利用了
-
Thread.sleep
- 不會釋放當前線程占有的鎖資源
- 在指定時間之后自動喚醒
- 可響應(yīng)
interrupt()
中斷,并且拋出InterruptedException
-
LockSupport.park
- 調(diào)用之前無需獲取鎖
- 調(diào)用之后不會釋放當前線程占有的鎖資源
- 可響應(yīng)
interrupt()
中斷,不會拋出InterruptedException
- 可調(diào)用
LockSupport.unpart(Thread)
指定喚醒某一線程鳄哭,并且一定會執(zhí)行后面的邏輯 -
LockSupport.unpart(Thread)
可在LockSupport.park
之前調(diào)用要糊,unpart
先調(diào)用之后纲熏,調(diào)用park
不會阻塞妆丘,會直接運行 - 多次調(diào)用
LockSupport.unpart(Thread)
,只能獲得一次許可局劲,無法疊加勺拣,只要調(diào)用LockSupport.park
就會被消費,隨后調(diào)用LockSupport.park
會被阻塞
關(guān)于鎖的釋放的補充
前面說到Object.wait
是會釋放鎖的鱼填,那么是釋放Object的鎖還是釋放線程持有的所有鎖呢药有?關(guān)于Object.wait
釋放鎖,我想你能搜到的99.9%的文章都是只說了這么一句——Object.wait
是會釋放鎖苹丸。先來看一個例子
class ThreadTest {
@Test
fun test() {
val lock = Object()
val lock2 = Object()
val thread = Thread1("thread 1",lock,lock2)
thread.start()
Thread.sleep(200)
synchronized(lock2){ //先獲取lock2看下會不會被釋放
synchronized(lock){
lock.notifyAll()
}
}
Thread.sleep(1000) //等待thread 1執(zhí)行完成
println("thread1 state :${thread.state}")
}
class Thread1 constructor(name: String,var lock:Object,var lock2:Object) : Thread(name){
override fun run() {
super.run()
synchronized(lock){ //持有l(wèi)ock2鎖
synchronized(lock2){ //持有l(wèi)ock2鎖
lock.wait()
println("$name 被喚醒")
}
}
}
}
}
- 猜猜上面的運行結(jié)果是什么愤惰? 會被喚醒嗎?
- 關(guān)于
Object.wait
會釋放synchronized鎖住的Object鎖毋庸置疑赘理,如果不能釋放的話也就沒有notify
一說了。實際上的運行結(jié)果是main thread 和 thread 1都阻塞了。仔細看一下線程的dump信息色建,可以看到thread 1處于WAITING狀態(tài)厦幅,并且它- locked <0x000000076be35ff8>
,而main thread被阻塞了蜘澜,處于BLOCKED,它- waiting to lock <0x000000076be35ff8>
。這不正是前面分析過的响疚,由于進入synchronized(lock2)
獲取不到鎖而處于BLOCKED狀態(tài)鄙信。所以說,Object.wait
實際上是調(diào)用哪個哪個對象的wait
方法忿晕,就釋放該對應(yīng)對象的鎖装诡。 - 那么既然
Thread.join
方法實際上是依賴于Object.wait
實現(xiàn)的,釋放哪個監(jiān)視器鎖也就清楚了