一題帶你徹底理解sleep()和wait()
一道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()
-
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 程式碼塊,釋放鎖後,其他的已經被喚醒的執行緒將會繼續競爭獲取該鎖,一直進行下去,直到所有被喚醒的執行緒都執行完畢。
sleep()不會釋放掉鎖(監控)
最開始的那道題答案是D
原文釋出時間為: 2018-11-16
本文作者: Java技術驛站
本文來自雲棲社群合作伙伴“ Java技術驛站”,瞭解相關資訊可以關注“ Java技術驛站”。
相關文章
- 一文帶你徹底理解 JavaScript 原型物件JavaScript原型物件
- 一文讓你徹底理解 Java HashMap 和 ConcurrentHashMapJavaHashMap
- sleep()和wait()區別AI
- 12張圖帶你徹底理解分散式事務!!分散式
- 帶你徹底弄懂Event LoopOOP
- wait() vs sleep()AI
- 一文讓你徹底理解 Java NIO 核心元件Java元件
- 【Java】sleep和wait區別總結JavaAI
- JavaScript之例題中徹底理解thisJavaScript
- 徹底理解ReentrantLockReentrantLock
- 徹底理解volatile
- 徹底理解synchronizedsynchronized
- 一篇文章帶你徹底搞懂join的用法
- Linux中Sleep和Wait命令的使用方式LinuxAI
- Spirit帶你徹底瞭解事件捕獲和冒泡機制事件
- 線段樹 - 多組圖帶你從頭到尾徹底理解線段樹
- 這一次,讓你徹底理解Java的值傳遞和引用傳遞!Java
- 畫圖帶你徹底弄懂三級快取和迴圈依賴的問題快取
- 徹底理解kubernetes CNI
- 徹底理解正則
- 徹底理解Golang MapGolang
- 「每天一道面試題」sleep方法和wait方法有什麼區別?面試題AI
- 這一次帶你徹底瞭解前端本地儲存前端
- 一張圖徹底理解Javascript原型鏈JavaScript原型
- 【日誌技術專題】「logback入門到精通」徹徹底底帶你學會logback框架的使用和原理(入門介紹篇)框架
- 【架構視角】一篇文章帶你徹底吃透Spring架構Spring
- 徹底理解JavaScript中的thisJavaScript
- 徹底理解cookie,session,tokenCookieSession
- setTimeout和setImmediate到底誰先執行,本文讓你徹底理解Event LoopOOP
- 計算機通訊之謎,帶你徹底理解socket網路程式設計(五)計算機程式設計
- 一文徹底理解微服務架構微服務架構
- 這一次,徹底理解XSS攻擊
- 12張圖帶你徹底理解分散式事務產生的場景和解決方案!!分散式
- 多執行緒面試題之sleep()和wait()方法有什麼區別?執行緒面試題AI
- 徹底理解連結器:四
- 小白(新手)如何徹底理解索引?索引
- 徹底理解 Dart mixin 機制Dart
- 徹底理解Hive中的鎖Hive