一題帶你徹底理解 sleep() 和 wait()

<code>轉 https://mp.weixin.qq.com/s?__biz=MzI5NTYwNDQxNA==&mid=2247484451&idx=1&sn=25e142a5b21921c887b38f48b4e022a7&chksm=ec505df2db27d4e496df215068f0abfa5054f44b2824a3210010f29611780309bb38cc8e9f0f&scene=21#wechat_redirect/<code>

一道Java的題目:

關於sleep()和wait(),以下描述錯誤的一項是:

A sleep是線程類(Thread)的方法,wait是Object類的方法;

B sleep不釋放對象鎖,wait放棄對象鎖

C sleep暫停線程、但監控狀態仍然保持,結束後會自動恢復

D wait後進入等待鎖定池,只有針對此對象發出notify方法後獲得對象鎖進入運行狀態

關於對象鎖:

截取網上的一段話:

所有對象都自動含有單一的鎖。JVM負責跟蹤對象被加鎖的次數。如果一個對象被解鎖,其計數變為0。在任務(線程)第一次給對象加鎖的時候,計數變為1。每當這個相同的任務(線程)在此對象上獲得鎖時,計數會遞增。 只有首先獲得鎖的任務(線程)才能繼續獲取該對象上的多個鎖。 每當任務離開一個synchronized(同步)方法,計數遞減,當計數為0的時候,鎖被完全釋放,此時別的任務就可以使用此資源。

這段話令人感到迷惑,一個對象不是隻有一個鎖嗎?只有獲得這個對象的鎖才能對它進行操作,若這個對象的鎖被一個線程先獲得,那就其他線程就需要等待。那多次加鎖什麼意思,鎖不是依附於對象的嗎?

在往下的文章中,我暫且理解為一個對象有且只有一把鎖,鎖在不同線程間傳遞,一個線程可以多次獲得同一個對象的鎖。暫且不考慮一個對象上多個鎖這種方法是不是確實存在,這對下面影響不大。

關於鎖池和等待池

在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類中的方法.

關於wait() ,notifyAll(),notify() 三個方法

wait()

<code>public final void wait() throws InterruptedException,IllegalMonitorStateException/<code>

該方法用來將當前線程置入休眠狀態,直到接到通知或被中斷為止。在調用 wait()之前,線程必須要獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用 wait()方法。進入 wait()方法後,當前線程釋放鎖。在從 wait()返回前,線程與其他線程競爭重新獲得鎖。如果調用 wait()時,沒有持有適當的鎖,則拋出 IllegalMonitorStateException,它是 RuntimeException 的一個子類,因此,不需要 try-catch 結構。

notify()

<code>public final native void notify() throws IllegalMonitorStateException/<code>

該方法也要在同步方法或同步塊中調用,即在調用前,線程也必須要獲得該對象的對象級別鎖,的如果調用 notify()時沒有持有適當的鎖,也會拋出 IllegalMonitorStateException。

該方法用來通知那些可能等待該對象的對象鎖的其他線程。如果有多個線程等待,則線程規劃器任意挑選出其中一個 wait()狀態的線程來發出通知,並使它等待獲取該對象的對象鎖(notify 後,當前線程不會馬上釋放該對象鎖,wait 所在的線程並不能馬上獲取該對象鎖,要等到程序退出 synchronized 代碼塊後,當前線程才會釋放鎖,wait所在的線程也才可以獲取該對象鎖),但不驚動其他同樣在等待被該對象notify的線程們。當第一個獲得了該對象鎖的 wait 線程運行完畢以後,它會釋放掉該對象鎖,此時如果該對象沒有再次使用 notify 語句,則即便該對象已經空閒,其他 wait 狀態等待的線程由於沒有得到該對象的通知,會繼續阻塞在 wait 狀態,直到這個對象發出一個 notify 或 notifyAll。這裡需要注意:它們等待的是被 notify 或 notifyAll,而不是鎖。這與下面的 notifyAll()方法執行後的情況不同。

notifyAll()

<code>public final native void notifyAll() throws IllegalMonitorStateException/<code>

該方法與 notify ()方法的工作方式相同,重要的一點差異是:

notifyAll 使所有原來在該對象上 wait 的線程統統退出 wait 的狀態(即全部被喚醒,不再等待 notify 或 notifyAll,但由於此時還沒有獲取到該對象鎖,因此還不能繼續往下執行),變成等待獲取該對象上的鎖,一旦該對象鎖被釋放(notifyAll 線程退出調用了 notifyAll 的 synchronized 代碼塊的時候),他們就會去競爭。如果其中一個線程獲得了該對象鎖,它就會繼續往下執行,在它退出 synchronized 代碼塊,釋放鎖後,其他的已經被喚醒的線程將會繼續競爭獲取該鎖,一直進行下去,直到所有被喚醒的線程都執行完畢。

sleep()不會釋放掉鎖(監控)

最開始的那道題答案是D



分享到:


相關文章: