1. 程式人生 > >Java多執行緒之wait,notify,sleep,,yield,join,suspend,resume

Java多執行緒之wait,notify,sleep,,yield,join,suspend,resume

Java中的多執行緒是一種搶佔式的機制而不是分時機制。執行緒主要有以下幾種狀態:可執行,執行,阻塞,死亡。搶佔式機制指的是有多個執行緒處於可執行狀態,但是隻有一個執行緒在執行。 當有多個執行緒訪問共享資料的時候,就需要對執行緒進行同步。執行緒中的幾個主要方法的比較:        Thread類的方法:sleep(),yield()        Object的方法:wait()notify() 每個物件都有一個機鎖來控制同步訪問Synchronized關鍵字可以和物件的機鎖互動,來實現執行緒的同步。 由於sleep()方法是Thread類的方法,因此它不能改變物件的機鎖。所以當在一個
Synchronized方法中呼叫sleep()時,執行緒雖然休眠了,但是物件的機鎖沒有被釋放,其他執行緒仍然無法訪問這個物件。而wait()方法則會線上程休眠的同時釋放掉機鎖,其他執行緒可以訪問該物件 Yield()方法是停止當前執行緒,讓同等優先權的執行緒執行。如果沒有同等優先權的執行緒,那麼Yield()方法將不會起作用。 一個執行緒結束的標誌是:run()方法結束。 一個機鎖被釋放的標誌是:synchronized塊或方法結束。        Wait()方法和notify()方法:當一個執行緒執行到wait()方法時(執行緒休眠且釋放機鎖),它就進入到一個和該物件相關的等待池中,同時失去了物件的機鎖。當它被一個
notify()方法喚醒時,等待池中的執行緒就被放到了鎖池中。該執行緒從鎖池中獲得機鎖,然後回到wait()前的中斷現場 join()方法使當前執行緒停下來等待,直至另一個呼叫join方法的執行緒終止。 值得注意的是:執行緒的在被啟用後不一定馬上就執行,而是進入到可執行執行緒的佇列中

共同點: 他們都是在多執行緒的環境下,都可以在程式的呼叫處阻塞指定的毫秒數,並返回。
不同點: Thread.sleep(long)可以不在synchronized的塊下呼叫,而且使用Thread.sleep()不會丟失當前執行緒對任何物件的同步鎖(monitor);


1、sleep()

使當前執行緒(即呼叫該方法的執行緒)暫停執行一段時間,讓其他執行緒有機會繼續執行,但它並不釋放物件鎖。也就是說如果有synchronized同步快,其他執行緒仍然不能訪問共享資料。注意該方法要捕捉異常。

例如有兩個執行緒同時執行(沒有synchronized)一個執行緒優先順序為MAX_PRIORITY,另一個為MIN_PRIORITY,如果沒有Sleep()方法,只有高優先順序的執行緒執行完畢後,低優先順序的執行緒才能夠執行;但是高優先順序的執行緒sleep(500)後,低優先順序就有機會執行了。

總之,sleep()可以使低優先順序的執行緒得到執行的機會,當然也可以讓同優先順序、高優先順序的執行緒有執行的機會。

2、join()

join()方法使呼叫該方法的執行緒在此之前執行完畢,也就是等待該方法的執行緒執行完畢後再往下繼續執行。注意該方法也需要捕捉異常。

3、yield()

該方法與sleep()類似,只是不能由使用者指定暫停多長時間,並且yield()方法只能讓同優先順序的執行緒有執行的機會。

4、wait()和notify()、notifyAll()

這三個方法用於協調多個執行緒對共享資料的存取,所以必須在synchronized語句塊內使用。synchronized關鍵字用於保護共享資料,阻止其他執行緒對共享資料的存取,但是這樣程式的流程就很不靈活了,如何才能在當前執行緒還沒退出synchronized資料塊時讓其他執行緒也有機會訪問共享資料呢?此時就用這三個方法來靈活控制。

wait()方法使當前執行緒暫停執行並釋放物件鎖標示,讓其他執行緒可以進入synchronized資料塊,當前執行緒被放入物件等待池中。當呼叫notify()方法後,將從物件的等待池中移走一個任意的執行緒並放到鎖標誌等待池中,只有鎖標誌等待池中執行緒能夠獲取鎖標誌;如果鎖標誌等待池中沒有執行緒,則notify()不起作用。

notifyAll()則從物件等待池中移走所有等待那個物件的執行緒並放到鎖標誌等待池中。

注意 這三個方法都是java.lang.Object的方法。

關於 wait() 和 notify() 方法最後再說明兩點:

    第一:呼叫 notify() 方法導致解除阻塞的執行緒是從因呼叫該物件的 wait() 方法而阻塞的執行緒中隨機選取的,我們無法預料哪一個執行緒將會被選擇,所以程式設計時要特別小心,避免因這種不確定性而產生問題。

    第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用,唯一的區別在於,呼叫 notifyAll() 方法將把因調 用該物件的 wait() 方法而阻塞的所有執行緒一次性全部解除阻塞。當然,只有獲得鎖的那一個執行緒才能進入可執行狀態。

二、run和start()

把需要處理的程式碼放到run()方法中,start()方法啟動執行緒將自動呼叫run()方法,這個由java的記憶體機制規定的。並且run()方法必需是public訪問許可權,返回值型別為void。

三、關鍵字synchronized

見前幾篇博文。

四、wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法。

(1)、常用的wait方法有wait()和wait(long timeout);

void wait() 在其他執行緒呼叫此物件的 notify() 方法或者 notifyAll()方法前,導致當前執行緒等待。

void wait(long timeout)在其他執行緒呼叫此物件的notify() 方法 或者 notifyAll()方法,或者超過指定的時間量前,導致當前執行緒等待。

wait()後,執行緒會釋放掉它所佔有的“鎖標誌”,從而使執行緒所在物件中的其他shnchronized資料可被別的執行緒使用。

wait()h和notify()因為會對物件的“鎖標誌”進行操作,所以他們必需在Synchronized函式或者 synchronized block 中進行呼叫。如果在non-synchronized 函式或 non-synchronized block 中進行呼叫,雖然能編譯通過,但在執行時會發生IllegalMonitorStateException的異常。。

(2)、Thread.sleep(long millis)必須帶有一個時間引數。

sleep(long)使當前執行緒進入停滯狀態,所以執行sleep()的執行緒在指定的時間內肯定不會被執行;

sleep(long)可使優先順序低的執行緒得到執行的機會,當然也可以讓同優先順序的執行緒有執行的機會;

sleep(long)是不會釋放鎖標誌的。

(3)、yield()沒有引數

sleep 方法使當前執行中的執行緒睡眠一段時間,進入不可以執行狀態,這段時間的長短是由程式設定的,yield方法使當前執行緒讓出CPU佔有權,但讓出的時間是不可設定的。

yield()也不會釋放鎖標誌。

實際上,yield()方法對應瞭如下操作;先檢測當前是否有相同優先順序的執行緒處於同可執行狀態,如有,則把CPU的佔有權交給次執行緒,否則繼續執行原來的執行緒,所以yield()方法稱為“退讓”,它把執行機會讓給了同等級的其他執行緒。

sleep 方法允許較低優先順序的執行緒獲得執行機會,但yield()方法執行時,當前執行緒仍處在可執行狀態,所以不可能讓出較低優先順序的執行緒此時獲取CPU佔有權。在一個執行系統中,如果較高優先順序的執行緒沒有呼叫sleep方法,也沒有受到I/O阻塞,那麼較低優先順序執行緒只能等待所有較高優先順序的執行緒執行結束,方可有機會執行。

yield()只是使當前執行緒重新回到可執行狀態,所有執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行,所以yield()方法只能使同優先順序的執行緒有執行的機會。

1) sleep()使當前執行緒進入停滯狀態,所以執行sleep()的執行緒在指定的時間內肯定不會執行;yield()只是使當前執行緒重新回到可執行狀態,所以執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行

2) sleep()可使優先順序低的執行緒得到執行的機會,當然也可以讓同優先順序和高優先順序的執行緒有執行的機會;yield()只能使同優先順序的執行緒有執行的機會。

resume和suspend已經被Java遺棄,因為他們天生會引起執行緒的死鎖。

suspend是個貪婪的傢伙,當一個執行緒在suspend的時候,執行緒會停下來,但卻仍然持有在這之前獲得的鎖定。其他執行緒無法使用他鎖定的任何資源,除非這個掛起的執行緒被resume之後,他才會繼續執行。對於執行緒的同步,使用wait與notify要安全的多。