leep()和wait()方法與物件鎖、鎖池、等待池
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/u014561933/article/details/58639411
一道Java的題目:
關於sleep()和wait(),以下描述錯誤的一項是: - A sleep是執行緒類(Thread)的方法,wait是Object類的方法; - B sleep不釋放物件鎖,wait放棄物件鎖 - C sleep暫停執行緒、但監控狀態仍然保持,結束後會自動恢復 - D wait後進入等待鎖定池,只有針對此物件發出notify方法後獲得物件鎖進入執行狀態
1.關於物件鎖:
擷取網上的一段話:
所有物件都自動含有單一的鎖。 JVM負責跟蹤物件被加鎖的次數。如果一個物件被解鎖,其計數變為0。在任務(執行緒)第一次給物件加鎖的時候,計數變為1。每當這個相同的任務(執行緒)在此物件上獲得鎖時,計數會遞增。 只有首先獲得鎖的任務(執行緒)才能繼續獲取該物件上的多個鎖。 每當任務離開一個synchronized(同步)方法,計數遞減,當計數為0的時候,鎖被完全釋放,此時別的任務就可以使用此資源。
這段話令人感到迷惑,一個物件不是隻有一個鎖嗎?只有獲得這個物件的鎖才能對它進行操作,若這個物件的鎖被一個執行緒先獲得,那就其他執行緒就需要等待。那多次加鎖什麼意思,鎖不是依附於物件的嗎? 在往下的文章中,我暫且理解為一個物件有且只有一把鎖,鎖在不同執行緒間傳遞,一個執行緒可以多次獲得同一個物件的鎖。暫且不考慮一個物件上多個鎖這種方法是不是確實存在,這對下面影響不大。
2.關於鎖池和等待池
在Java中,每個物件都有兩個池,鎖(monitor)池和等待池
-
鎖池:假設執行緒A已經擁有了某個物件(注意:不是類)的鎖,而其它的執行緒想要呼叫這個物件的某個synchronized方法(或者synchronized塊),由於這些執行緒在進入物件的synchronized方法之前必須先獲得該物件的鎖的擁有權,但是該物件的鎖目前正被執行緒A擁有,所以這些執行緒就進入了該物件的鎖池中。
-
等待池:假設一個執行緒A呼叫了某個物件的wait()方法,執行緒A就會釋放該物件的鎖(因為wait()方法必須出現在synchronized中,這樣自然在執行wait()方法之前執行緒A就已經擁有了該物件的鎖),同時執行緒A就進入到了該物件的等待池中。如果另外的一個執行緒呼叫了相同物件的notifyAll()方法,那麼處於該物件的等待池中的執行緒就會全部進入該物件的鎖池中,準備爭奪鎖的擁有權。如果另外的一個執行緒呼叫了相同物件的notify()方法,那麼僅僅有一個處於該物件的等待池中的執行緒(隨機)會進入該物件的鎖池.
深入理解: 如果執行緒呼叫了物件的 wait()方法,那麼執行緒便會處於該物件的等待池中,等待池中的執行緒不會去競爭該物件的鎖。 當有執行緒呼叫了物件的 notifyAll()方法(喚醒所有 wait 執行緒)或 notify()方法(只隨機喚醒一個 wait 執行緒),被喚醒的的執行緒便會進入該物件的鎖池中,鎖池中的執行緒會去競爭該物件鎖。 優先順序高的執行緒競爭到物件鎖的概率大,假若某執行緒沒有競爭到該物件鎖,它還會留在鎖池中,唯有執行緒再次呼叫 wait()方法,它才會重新回到等待池中。而競爭到物件鎖的執行緒則繼續往下執行,直到執行完了 synchronized 程式碼塊,它會釋放掉該物件鎖,這時鎖池中的執行緒會繼續競爭該物件鎖。
注:wait() ,notifyAll(),notify() 三個方法都是Object類中的方法.
3.關於wait() ,notifyAll(),notify() 三個方法
- wait() public final void wait() throws InterruptedException,IllegalMonitorStateException
該方法用來將當前執行緒置入休眠狀態,直到接到通知或被中斷為止。在呼叫 wait()之前,執行緒必須要獲得該物件的物件級別鎖,即只能在同步方法或同步塊中呼叫 wait()方法。進入 wait()方法後,當前執行緒釋放鎖。在從 wait()返回前,執行緒與其他執行緒競爭重新獲得鎖。如果呼叫 wait()時,沒有持有適當的鎖,則丟擲 IllegalMonitorStateException,它是 RuntimeException 的一個子類,因此,不需要 try-catch 結構。
- notify() public final native void notify() throws IllegalMonitorStateException
該方法也要在同步方法或同步塊中呼叫,即在呼叫前,執行緒也必須要獲得該物件的物件級別鎖,的如果呼叫 notify()時沒有持有適當的鎖,也會丟擲 IllegalMonitorStateException。
該方法用來通知那些可能等待該物件的物件鎖的其他執行緒。如果有多個執行緒等待,則執行緒規劃器任意挑選出其中一個 wait()狀態的執行緒來發出通知,並使它等待獲取該物件的物件鎖(notify 後,當前執行緒不會馬上釋放該物件鎖,wait 所在的執行緒並不能馬上獲取該物件鎖,要等到程式退出 synchronized 程式碼塊後,當前執行緒才會釋放鎖,wait所在的執行緒也才可以獲取該物件鎖),但不驚動其他同樣在等待被該物件notify的執行緒們。當第一個獲得了該物件鎖的 wait 執行緒執行完畢以後,它會釋放掉該物件鎖,此時如果該物件沒有再次使用 notify 語句,則即便該物件已經空閒,其他 wait 狀態等待的執行緒由於沒有得到該物件的通知,會繼續阻塞在 wait 狀態,直到這個物件發出一個 notify 或 notifyAll。這裡需要注意:它們等待的是被 notify 或 notifyAll,而不是鎖。這與下面的 notifyAll()方法執行後的情況不同。
- notifyAll() public final native void notifyAll() throws IllegalMonitorStateException
該方法與 notify ()方法的工作方式相同,重要的一點差異是:
notifyAll 使所有原來在該物件上 wait 的執行緒統統退出 wait 的狀態(即全部被喚醒,不再等待 notify 或 notifyAll,但由於此時還沒有獲取到該物件鎖,因此還不能繼續往下執行),變成等待獲取該物件上的鎖,一旦該物件鎖被釋放(notifyAll 執行緒退出呼叫了 notifyAll 的 synchronized 程式碼塊的時候),他們就會去競爭。如果其中一個執行緒獲得了該物件鎖,它就會繼續往下執行,在它退出 synchronized 程式碼塊,釋放鎖後,其他的已經被喚醒的執行緒將會繼續競爭獲取該鎖,一直進行下去,直到所有被喚醒的執行緒都執行完畢。
4.sleep()不會釋放掉鎖(監控) 最開始的那道題答案是D