線程的調度
線程的調度是指系統為線程分配處理器使用權的過程,主要調度方式有兩種
協同式線程調度
線程執行時間由線程本身來控制,線程把自己的工作執行完了之后,要主動通知系統切到另一個線程上。
協調式多線程的最大的好處是實現簡單,而且由於線程要把自己的事情干完之后才會進行線程切換,切換操作對線程自己是可知的,所以沒有什么線程同步的問題。
缺點也很明顯:線程執行時間不可控制,如果有一個線程編寫有問題,一直不告知系統進行線程切換,那么程序就會一直阻塞在那里。
搶占式線程調度
每個線程將由系統分配執行時間,線程的切換不由線程本身來決定(JAVA中,Thread.yield()可以讓出執行時間)
雖然java線程調度是系統自動完成的,但是我們還是可以"建議"系統給某些線程多一些執行時間,這項操作可以通過設置線程優先級完成(Thread.MIN_PRIORITY致Thread.Thread.MAX_PRIORITY)
線程狀態轉換關系
線程狀態圖有助於我們記憶線程一些方法。
java語言定義了5種線程狀態,在任意一個時間點,一個線程只能有且只有其中的一種狀態,這5種狀態分別如下。
1)新建(New):
創建后尚未啟動的線程處於這種狀態。
2)運行(Runable):
Runable包括了操作系統線程狀態中的Running和Ready,也就是處於此狀態的線程有可能正在執行,也有可能等待着CPU為它分配執行時間,所謂的Ready(就緒)狀態 指的在CPU的等待隊列里面。
Thread.yield()會從Running 狀態—>Ready狀態。yield(): 當前線程會做出讓出CPU使用的暗示,但是線程調度器可能會忽略這個暗示
3)等待(Waiting):
無限期等待(Waiting):
處於這種狀態的線程不會被分配CPU執行時間,它們要等待被其他線程顯式地喚醒。以下方法會讓線程陷入無限期的等待狀態
沒有設置Timeout參數的Object.wait()方法
wait():的目的是為了多個線程搶資源的時候,當前線程把資源讓出來
如果沒有鎖,就不存在兩個線程搶資源的情況,也就沒有必要把資源讓出來,兩個線程早就一起跑了 這也很好解釋了為什么wait方法synchronized關鍵字連用。
沒有設置Timeout參數的Thread.join()方法
join():合並某個線程,他調用此方法的線程(別的線程)合並到當前線程上來。等待調用此方法的線程執行完了,當前線程才開始繼續執行(經常用於等待另外一個線程的結束,有點像方法調用)。
LockSupport.park()方法 參考LockSupport實現生產者與消費者
限期等待(Time Waiting):
處於這種狀態的線程也不會被CPU分配執行時間,不過無須等待被其他線程顯式的喚醒,在一定時間之后它們會由系統自動喚醒。以下方法會讓線程進入限期等待的狀態
Thread.sleep()方法
sleep():的過程之中 如果對當前對象加鎖了(可以不加),不會釋放當前對象的鎖。
設置了Timeout的Object.wait()方法
設置了TImeout的Thread.join()方法
LockSupport.parkBanos()方法和LockSupport.parkUntil()方法
4)阻塞(Blocked):
"阻塞狀態"與"等待狀態"的區別是:阻塞狀態在等待着獲取到一個排他鎖,等待着一個進入同步代碼塊的鎖;而等待狀態則是等待一段時間,或者喚醒動作的發生。
5)結束(Terminated):
已經終止線程的狀態,線程已經結束執行。
上述5種狀態在遇到特定事件發生的時候會互相轉換,它們之間的關系如下圖所示: