《Thread之一:線程生命周期及五種狀态》
《Thread之二:sleep、wait、yield、join》
《juc線程池原理(四): 線程池狀态介紹》
一、回顧下概念
Java中的多線程是一種搶占式的機制而不是分時機制。線程主要有以下幾種狀态:建立,就緒,運作,阻塞,死亡。搶占式機制指的是有多個線程處于就緒狀态,但是隻有一個線程在運作。

1.sleep()方法
在指定時間内讓目前正在執行的線程暫停執行,但不會釋放“鎖标志”。不推薦使用。
sleep()使目前線程進入阻塞狀态,在指定時間内不會執行。
2.wait()方法
在其他線程調用對象的notify或notifyAll方法前,導緻目前線程等待。線程會釋放掉它所占有的“鎖标志”,進而使别的線程有機會搶占該鎖。
目前線程必須擁有目前對象鎖。如果目前線程不是此鎖的擁有者,會抛出IllegalMonitorStateException異常。
喚醒目前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,否則也會抛出IllegalMonitorStateException異常。
waite() 和notify()必須在synchronized函數或synchronized block中進行調用。如果在non-synchronized函數或non-synchronized block中進行調用,雖然能編譯通過,但在運作時會發生 IllegalMonitorStateException的異常。
3.yield方法
暫停目前正在執行的線程對象。
yield()隻是使目前線程重新回到可執行狀态,是以執行yield()的線程有可能在進入到可執行狀态後馬上又被執行。
yield()隻能使同優先級或更高優先級的線程有執行的機會。
4.join方法
等待該線程終止。
等待調用join方法的線程結束,再繼續執行。如:t.join();//主要用于等待t線程運作結束,若無此句,main則會執行完畢,導緻結果不可預測。
二、線程同步
一個線程結束的标志是:run()方法結束。
一個機鎖被釋放的标志是:synchronized塊或方法結束。
當有多個線程通路共享資料的時候,就需要對線程進行同步。線程同步相關的方法中的幾個主要方法的按照所屬可以分成:
- Thread類的方法:sleep(),yield()等
- Object的方法:wait()和notify()、notifyAll()等
Object中的對象頭存放的鎖資訊在控制同步通路時使用。見《java對象在記憶體中的結構(HotSpot虛拟機)》和《Synchronized之二:synchronized的實作原理》
Wait()方法和notify()方法:當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去了對象的機鎖。當它被一個notify()方法喚醒時,等待池中的線程就被放到了鎖池中。該線程從鎖池中獲得機鎖,然後回到wait()前的中斷現場。
Thread類中的方法:
由于sleep()方法是Thread類的方法,是以它不能改變對象的機鎖。是以當在一個Synchronized方法中調用sleep()時,線程雖然休眠了,但是對象的機鎖沒有被釋放,其他線程仍然無法通路這個對象。而wait()方法則會線上程休眠的同時釋放掉機鎖,其他線程可以通路該對象。
Yield()方法:是停止目前線程,讓同等優先權的線程運作。如果沒有同等優先權的線程,那麼Yield()方法将不會起作用。
join()方法:是由一個線程調用另一個線程,調用線程等待被調用線程終止。
sleep()與wait()的共同點及不同點:
共同點: 他們都是在多線程的環境下,都可以在程式的調用處阻塞指定的毫秒數,并傳回。
不同點: Thread.sleep(long)可以不在synchronized的塊下調用,而且使用Thread.sleep()不會丢失目前線程對任何對象的同步鎖(monitor);
object.wait(long)必須在synchronized的塊下來使用,調用了之後失去對object的monitor, 這樣做的好處是它不影響其它的線程對object進行操作。
舉個java.util.Timer的例子來說明。
private void main Loop() {
while (true) {
....
synchronized(queue) {
.....
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
}
private void sched(TimerTask task, long time, long period) {
synchronized(queue) {
...
queue.add(task);
}
}