JUC中的wait與notify方法實(shí)現(xiàn)原理詳解
1.Object中的wait()實(shí)現(xiàn)原理
在進(jìn)行wait()之前,就代表著需要爭(zhēng)奪Synchorized,而Synchronized代碼塊通過(guò)javap生成的字節(jié)碼中包含monitor enter
和monitor exit
兩個(gè)指令。
當(dāng)在進(jìn)加鎖的時(shí)候會(huì)執(zhí)行monitor enter
指令,執(zhí)行該指令可以獲取對(duì)象的monitor
。同時(shí)在執(zhí)行Lock.wait()的時(shí)候也必須持有monitor對(duì)象。
在多核環(huán)境下,多個(gè)線(xiàn)程有可能同時(shí)執(zhí)行monitor enter
指令,并獲取lock對(duì)象關(guān)聯(lián)的monitor,但只有一個(gè)線(xiàn)程可以和monitor建立關(guān)聯(lián),這個(gè)線(xiàn)程執(zhí)行到wait方法時(shí),wait方法會(huì)將當(dāng)前線(xiàn)程放入wait set,使其進(jìn)行等待直到被喚醒,并放棄lock對(duì)象上的所有同步聲明,意味著該線(xiàn)程釋放了鎖,其他線(xiàn)程可以重新執(zhí)行加鎖操作,notify方法會(huì)選擇wait set中任意一個(gè)線(xiàn)程進(jìn)行喚醒,notifyAll方法會(huì)喚醒monitor的wait set
中所有線(xiàn)程。執(zhí)行完notify方法并不會(huì)立馬喚醒等待線(xiàn)程。那么wait具體是怎么實(shí)現(xiàn)的呢?
首先在HotSpot虛擬機(jī)中,monitor采用ObjectMonitor實(shí)現(xiàn),每個(gè)線(xiàn)程都具有兩個(gè)隊(duì)列,分別為free和used,用來(lái)存放ObjectMonitor。如果當(dāng)前free列表為空,線(xiàn)程將向全局global list請(qǐng)求分配ObjectMonitor。
ObjectMonitor對(duì)象中有兩個(gè)隊(duì)列,都用來(lái)保存ObjectWaiter對(duì)象,分別是_WaitSet 和 _EntrySet。_owner用來(lái)指向獲得ObjectMonitor對(duì)象的線(xiàn)程
ObjectWaiter對(duì)象是雙向鏈表結(jié)構(gòu),保存了_thread(當(dāng)前線(xiàn)程)以及當(dāng)前的狀態(tài)TState等數(shù)據(jù), 每個(gè)等待鎖的線(xiàn)程都會(huì)被封裝成ObjectWaiter對(duì)象。
_WaitSet
:處于wait狀態(tài)的線(xiàn)程,會(huì)被加入到wait set;
_EntrySett
:處于等待鎖block狀態(tài)的線(xiàn)程,會(huì)被加入到entry set;
wait方法實(shí)現(xiàn)
lock.wait()方法最終通過(guò)ObjectMonitor的 wait(jlong millis, bool interruptable, TRAPS)實(shí)現(xiàn)
1、將當(dāng)前線(xiàn)程封裝成ObjectWaiter對(duì)象node
2、通過(guò)ObjectMonitor::AddWaiter方法將node添加到_WaitSet列表中
3、通過(guò)ObjectMonitor::exit方法釋放當(dāng)前的ObjectMonitor對(duì)象,這樣其它競(jìng)爭(zhēng)線(xiàn)程就可以獲取該ObjectMonitor對(duì)象
4、最終底層的park方法會(huì)掛起線(xiàn)程
ObjectSynchorizer::wait方法通過(guò)Object對(duì)象找到ObjectMonitor對(duì)象來(lái)調(diào)用方法 ObjectMonitor::wait(),通過(guò)調(diào)用ObjectMonitor::AddWaiter()可以把新建的ObjectWaiter對(duì)象,放入到_WaitSet隊(duì)列的末尾,然后在ObjectMonitor::exit釋放鎖,接著通過(guò)執(zhí)行thread_ParkEvent->park來(lái)掛起線(xiàn)程,也就是進(jìn)行wait。
2.Object對(duì)象中的wait,notify,notifyAll的理解
wait,notify,notifyAll 是定義在Object類(lèi)的實(shí)例方法,用于控制線(xiàn)程狀態(tài),在線(xiàn)程協(xié)作時(shí),大家都會(huì)用到notify()或者notifyAll()方法,其中wait與notify是java同步機(jī)制中重要的組成部分,需要結(jié)合與synchronized關(guān)鍵字才能使用,在調(diào)用一個(gè)Object的wait與notify/notifyAll的時(shí)候,必須保證調(diào)用代碼對(duì)該Object是同步的,也就是說(shuō)必須在作用等同于synchronized(object){…}的內(nèi)部才能夠去調(diào)用obj的wait與notify/notifyAll三個(gè)方法,否則就會(huì)報(bào)錯(cuò):java.lang.IllegalMonitorStateException:current thread not owner(意思是因?yàn)闆](méi)有同步,所以線(xiàn)程對(duì)對(duì)象鎖的狀態(tài)是不確定的,不能調(diào)用這些方法)。
wait的目的就在于暴露出對(duì)象鎖,所以需要保證在lock的同步代碼中調(diào)用lock.wait()方法,讓其他線(xiàn)程可以通過(guò)對(duì)象的notify叫醒等待在該對(duì)象的等該池里的線(xiàn)程。同樣notify也會(huì)釋放對(duì)象鎖,在調(diào)用之前必須獲得對(duì)象的鎖,不然也會(huì)報(bào)異常。所以,在線(xiàn)程自動(dòng)釋放其占有的對(duì)象鎖后,不會(huì)去申請(qǐng)對(duì)象鎖,只有當(dāng)線(xiàn)程被喚醒的時(shí)候或者達(dá)到最大的睡眠時(shí)間,它才再次爭(zhēng)取對(duì)象鎖的權(quán)利
主要方法:
wait()
等待對(duì)象的同步鎖,需要獲得該對(duì)象的同步鎖才可以調(diào)用這個(gè)方法,否則編譯可以通過(guò),但運(yùn)行時(shí)會(huì)收到一個(gè)異常:IllegalMonitorStateException。調(diào)用任意對(duì)象的 wait() 方法導(dǎo)致該線(xiàn)程阻塞,該線(xiàn)程不可繼續(xù)執(zhí)行,并且該對(duì)象上的鎖被釋放。
notify()
喚醒在等待該對(duì)象同步鎖的線(xiàn)程(只喚醒一個(gè),如果有多個(gè)在等待),注意的是在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線(xiàn)程,而是由JVM確定喚醒哪個(gè)線(xiàn)程,而且不是按優(yōu)先級(jí)。調(diào)用任意對(duì)象的notify()方法則導(dǎo)致因調(diào)用該對(duì)象的 wait()方法而阻塞的線(xiàn)程中隨機(jī)選擇的一個(gè)解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。
notifyAll()
喚醒所有等待的線(xiàn)程,注意喚醒的是notify之前wait的線(xiàn)程,對(duì)于notify之后的wait線(xiàn)程是沒(méi)有效果的。
3.wait 實(shí)戰(zhàn)
通過(guò)一個(gè)實(shí)例來(lái)看一下實(shí)際的效果,開(kāi)啟兩個(gè)線(xiàn)和,一個(gè)線(xiàn)程 打印1到52的數(shù)字,一個(gè)打印A到Z的字母,要求,打印兩個(gè)數(shù),打印一個(gè)字母,這樣交替順序打印,代碼如下:
public class ShuZiZiMuThread { public static void main(String[] args) { Object object = new Object(); shuzi shuzi = new shuzi(object); zimu zimu = new zimu(object); Thread t = new Thread(shuzi); t.setName("shuzi"); Thread t1 = new Thread(zimu); t1.setName("zimu"); t.start();//數(shù)字線(xiàn)程先運(yùn)行 t1.start(); } } class shuzi implements Runnable{ private Object object; //聲明類(lèi)的引用 public shuzi(Object object) { this.object = object; } public void run() { synchronized (object) {//上鎖 for(int i=1;i<53;i++){ System.out.print(i+","); if(i%2==0){ object.notifyAll();//喚醒其它爭(zhēng)奪權(quán)限的線(xiàn)程 try { object.wait();//釋放鎖,進(jìn)入等待 System.out.println("數(shù)字打印類(lèi)打全打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程"+Thread.currentThread().getName());//輸出當(dāng)前擁有鎖的線(xiàn)程名稱(chēng) } catch (InterruptedException e) { e.printStackTrace(); } } } } } } class zimu implements Runnable{ private Object object; public zimu(Object object) { this.object = object; } public void run() { synchronized (object) { for(int j=65;j<91;j++){ char c = (char)j; System.out.print(c); object.notifyAll();//喚醒其它爭(zhēng)奪權(quán)限的線(xiàn)程 try { object.wait();//釋放鎖,進(jìn)入等待 System.out.println("字母打印類(lèi)打全打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程"+Thread.currentThread().getName());//輸出當(dāng)前擁有鎖的線(xiàn)程名稱(chēng) } catch (InterruptedException e) { e.printStackTrace(); } } } } }
實(shí)際運(yùn)行的結(jié)果 :
1,2,A數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
3,4,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
B數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
5,6,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
C數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
7,8,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
D數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
9,10,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
E數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
11,12,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
F數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
13,14,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
G數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
15,16,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
H數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
17,18,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
I數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
19,20,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
J數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
21,22,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
K數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
23,24,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
L數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
25,26,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
M數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
27,28,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
N數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
29,30,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
O數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
31,32,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
P數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
33,34,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
Q數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
35,36,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
R數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
37,38,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
S數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
39,40,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
T數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
41,42,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
U數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
43,44,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
V數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
45,46,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
W數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
47,48,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
X數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
49,50,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
Y數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
51,52,字母打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程zimu
Z數(shù)字打印類(lèi)打印當(dāng)前對(duì)象擁有對(duì)象鎖的線(xiàn)程shuzi
結(jié)果分析:
通過(guò)結(jié)果可以看出:
在字母打一打印類(lèi)里 調(diào)用完
通過(guò)結(jié)果可以看出:
在字母打一打印類(lèi)里 調(diào)用完
到此這篇關(guān)于JUC中的wait()與notify()方法實(shí)現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)JUC wait()與notify()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java多線(xiàn)程死鎖問(wèn)題詳解(wait和notify)
- Java使用wait/notify實(shí)現(xiàn)線(xiàn)程間通信下篇
- Java使用wait/notify實(shí)現(xiàn)線(xiàn)程間通信上篇
- Java多線(xiàn)程wait()和notify()方法詳細(xì)圖解
- Java中notify是順序喚醒還是隨機(jī)喚醒的
- Java使用wait和notify實(shí)現(xiàn)線(xiàn)程之間的通信
- Java線(xiàn)程通信之wait-notify通信方式詳解
- Java中notify和notifyAll的區(qū)別及何時(shí)使用
相關(guān)文章
解決springboot項(xiàng)目啟動(dòng)報(bào)錯(cuò)Field xxxMapper in com...xx
這篇文章主要介紹了解決springboot項(xiàng)目啟動(dòng)報(bào)錯(cuò)Field xxxMapper in com...xxxContr問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12Spring Boot 定制與優(yōu)化內(nèi)置的Tomcat容器實(shí)例詳解
本文主要記錄對(duì)內(nèi)置容器優(yōu)化和定制的方式,用于自己加深對(duì)SpringBoot理解。本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-12-12java的poi技術(shù)讀取和導(dǎo)入Excel實(shí)例
本篇文章主要介紹了java的poi技術(shù)讀取和導(dǎo)入Excel實(shí)例,報(bào)表輸出是Java應(yīng)用開(kāi)發(fā)中經(jīng)常涉及的內(nèi)容,有需要的可以了解一下。2016-11-11使用spring整合Quartz實(shí)現(xiàn)—定時(shí)器功能
這篇文章主要介紹了使用spring整合Quartz實(shí)現(xiàn)—定時(shí)器功能,不基于特定的基類(lèi)的方法,需要的朋友可以參考下2018-04-04Springboot使用thymeleaf動(dòng)態(tài)模板實(shí)現(xiàn)刷新
這篇文章主要介紹了Springboot使用thymeleaf動(dòng)態(tài)模板實(shí)現(xiàn)刷新,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java中解密微信加密數(shù)據(jù)工具類(lèi)
最近小編一直在開(kāi)發(fā)微信公眾號(hào)、小程序項(xiàng)目,微信返回給我們的數(shù)據(jù)都是加密的,我們需要使用sessionkey配合解密,才能看到我們想要的數(shù)據(jù),基于代碼怎么實(shí)現(xiàn)呢,下面小編給大家?guī)?lái)了Java中解密微信加密數(shù)據(jù)工具類(lèi)的完整代碼,一起看看吧2021-06-06為什么阿里要慎重使用ArrayList中的subList方法
這篇文章主要介紹了為什么要慎重使用ArrayList中的subList方法,subList是List接口中定義的一個(gè)方法,該方法主要用于返回一個(gè)集合中的一段、可以理解為截取一個(gè)集合中的部分元素,他的返回值也是一個(gè)List。,需要的朋友可以參考下2019-06-06