在java中,很多時候我們忽略的基本的知識,這是很致命的,只有搞懂Thread的基礎知識,才能進一步探索:reentrantLock,AQS等。
1:Thread的線程狀態到底有幾種?
6種:
public enum State {/*** Thread state for a thread which has not yet started.尚未啟動的線程的線程狀態。*/NEW,/*** Thread state for a runnable thread. 可運行線程的線程狀態。*/RUNNABLE,/*** 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* 等待獲取鎖的狀態,或者等待獲取重入鎖的狀態*/BLOCKED,/*** Thread state for a waiting thread. 線程等待狀態調用以下方法都可以使一個線程進入等待狀態 -》等待后必須通過notify(),notifyAll()才能喚醒,喚醒后才能重新去嘗試獲取CPU的執行權* <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>*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* 有期限的等待,超時等待。以下方法:* sleep Thread.sleep* Object#wait(long) * join(long) Thread.join* LockSupport#parkNanos LockSupport.parkNanos* LockSupport#parkUntil LockSupport.parkUntil*/TIMED_WAITING,/*** Thread state for a terminated thread.* 線程結束*/TERMINATED;}
以上源碼就解釋了什么是阻塞,等待,但是沒有掛起,為什么沒有掛起狀態,因為線程掛起這種操作已經過時,不建議使用了,這里不做過多討論,有人愿意研究的,請自行搜索:線程掛起。不要搜跟阻塞,等待的區別,很多都說錯了。
2:到底如何區分阻塞,等待?
一切回歸原始:假設一個場景:多個線程任務執行在同一個單核cpu上,在我們獲取臨界資源的時候,為了線程安全,是要加鎖的,假設鎖是synchronized,那么同一時刻,只有一個線程任務能獲取鎖資源,那么其它的線程就進入了:blocked狀態,等待獲取鎖的狀態,如上代碼BLOCKED,? 此時,這些沒有獲取到鎖的線程有一些條件:1:他們都是可以隨時被CPU上下文切換獲取到執行權的。2:他們雖然可以獲取CPU執行的時間片,但是他們無法獲取鎖。所以被阻塞在這里了。
基于以上兩點,那么他們處于阻塞狀態。而第一個獲取到鎖的線程,是runnable狀態。
如果有大量線程為了競爭同一把鎖(同一臨界資源)而發生大面積阻塞,就會形成線程饑餓。
那什么是等待?還是上面的場景,加入有一個線程(第一個線程)獲取到了鎖,那么其它任何線程嘗試獲取鎖都會失敗,一旦失敗,調用方法讓他們進入等待的話,他們就不能再獲取到CPU執行權了,跟上面所說的阻塞特征的第一點沖突,另外,他們也不會一直嘗試去獲取鎖了,因為他們拿不到CPU執行權,相當于一個癱瘓的人沒有CPU的幫扶,是無法站起來的。 那么他們如何才能站起來,繼續有機會獲取CPU執行權,進而獲取鎖呢? --喚醒-》notify。 所以,假如一個線程是wait狀態已經,如果沒有任何線程去喚醒它,那它永遠是死的,如果大量的線程一旦永無機會被喚醒的話,將會占用大量內存。
到這里我們一起看一下我sleep和wait的區別
sleep和wait_肥春勿擾的博客-CSDN博客