<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
問題:
1.執行緒 wait()方法使用有什麼前提?
2. 多執行緒之間如何進行通訊?
3. Java 中 notify 和 notifyAll 有什麼區別?
4. 為什麼 wait/notify/notifyAll 這些方法不在 thread 類裡面?
5. 為什麼 wait 和 notify 方法要在同步塊中呼叫?
6. notify()和 notifyAll()有什麼區別?
執行緒是作業系統排程的最小單位,有自己的棧空間,可以按照既定的程式碼逐步執行,但是如果每個執行緒間都孤立地執行,就會造資源浪費。所以在現實中,如果需要多個執行緒按照指定的規則共同完成一個任務,那麼這些執行緒之間就需要互相協調,這個過程被稱為執行緒的通訊。
執行緒的通訊可以被定義為:當多個執行緒共同操作共用的資源時,執行緒間通過某種方式互相告知自己的狀態,以避免無效的資源爭奪。
執行緒間通訊的方式可以有很多種:等待-通知、共用記憶體、管道流。“等待-通知”通訊方式是Java中使用普遍的執行緒間通訊方式,其經典的案例是“生產者-消費者”模式。
場景:有幾個小孩都想進入房間內使用算盤(CPU)進行計算,老王(作業系統)就使用了一把鎖(synchronized)讓同一時間只有一個小孩能進入房間使用算盤,於是他們排隊進入房間。
(1) 小南最先獲取到了鎖,進入到房間內,但是由於條件不滿足(沒煙幹不了活),小南不能繼續進行計算 ,但小南如果一直佔用著鎖,其它人就得一直阻塞,效率太低。
(2) 於是老王單開了一間休息室(呼叫 wait 方法),讓小南到休息室(WaitSet)等著去了,這時鎖釋放開, 其它人可以由老王隨機安排進屋
(3) 直到小M將煙送來,大叫一聲 [ 你的煙到了 ] (呼叫 notify 方法)
(4) 小南於是可以離開休息室,重新進入競爭鎖的佇列
java語言中“等待-通知”方式的執行緒間通訊使用物件的wait()、notify()
兩類方法來實現。每個java物件都有wait()、notify(
)兩類實體方法,並且wait()、notify()
方法和物件的監視器是緊密相關的。
wait()、notify()
兩類方法在數量上不止兩個。wait()、notify()
兩類方法不屬於Thread類,而是屬於java物件範例。
java
物件中的wait()、notify()
兩類方法就如同訊號開關,用於等待方和通知方之間的互動。
物件的wait()
方法的主要作用是讓當前執行緒阻塞並等待被喚醒。wait()
方法與物件監視器緊密相關,使用wait()
方法時一定要放在同步塊中。wait()方法的呼叫方法如下:
public class Main { static final Object lock = new Object(); public static void method1() throws InterruptedException { synchronized( lock ) { lock.wait(); } } }
Object類中的wait()方法有三個版本:
(1) void wait()
:當前執行緒呼叫了同步物件lock
的wait()
實體方法後,將導致當前的執行緒等待,當前執行緒進入lock的監視器WaitSet,等待被其他執行緒喚醒;
(2) void wait(long timeout)
:限時等待。導致當前的執行緒等待,等待被其他執行緒喚醒,或者指定的時間timeout用完,執行緒不再等待;
(3) void wait(long timeout,int nanos)
:高精度限時等待,其主要作用是更精確地控制等待時間。引數nanos是一個附加的納秒級別的等待時間;
物件的notify()
方法的主要作用是喚醒在等待的執行緒。notify()方法與物件監視器緊密相關,呼叫notify()方法時也需要放在同步塊中。notify()方法的呼叫方法如下:
public class Main { static final Object lock = new Object(); public static void method1() throws InterruptedException { synchronized( lock ) { lock.notify(); } } }
notify()方法有兩個版本:
(1)void notify()
:lock.notify()
呼叫後,喚醒lock監視器等待集中的第一條等待執行緒;被喚醒的執行緒進入EntryList,其狀態從WAITING變成BLOCKED。
(2) void notifyAll()
:lock.notifyAll()
被呼叫後,喚醒lock監視器等待集中的全部等待執行緒,所有被喚醒的執行緒進入EntryList,執行緒狀態從WAITING變成BLOCKED。
小結:
obj.wait()
:讓進入Object監視器的執行緒到waitset等待
obj.notify()
:在Object上正在waitset等待的執行緒中挑一個喚醒
obj.notifyAll()
:讓在Object上正在waitset等待的執行緒全部喚醒
物件的wait()方法的核心原理大致如下:
(1) 當執行緒呼叫了lock
(某個同步鎖物件)的wait()
方法後,jvm會將當前執行緒加入lock
監視器的WaitSet(等待集),等待被其他執行緒喚醒。
(2) 當前執行緒會釋放lock
物件監視器的Owner權利,讓其他執行緒可以搶奪lock
物件的監視器。
(3) 讓當前執行緒等待,其狀態變成WAITING。線上程呼叫了同步物件lock的wait()
方法之後,同步物件lock
的監視器內部狀態大致如圖2-15所示。
物件的notify()或者notifyAll()方法的原理大致如下:
(1) 當執行緒呼叫了lock
(某個同步鎖物件)的notify()
方法後,jvm會喚醒lock
監視器WaitSet中的第一條等待執行緒。
(2) 當執行緒呼叫了lock
的notifyAll()
方法後,jvm會喚醒lock監視器WaitSet中的所有等待執行緒。
(3) 等待執行緒被喚醒後,會從監視器的WaitSet移動到EntryList,執行緒具備了排隊搶奪監視器Owner權利的資格,其狀態從WAITING變成BLOCKED。
(4) EntryList中的執行緒搶奪到監視器的Owner權利之後,執行緒的狀態從BLOCKED變成Runnable,具備重新執行的資格。
(1) Owner 執行緒發現條件不滿足,呼叫wait
方法,即可進入WaitSet,變為 WAITING 狀態 ;
(2) BLOCKED 和WAITING 的執行緒都處於阻塞狀態,不佔用CPU時間片 ;
(3) BLOCKED:執行緒會在Owner 執行緒釋放鎖時喚醒 ;
(4) WAITING :執行緒會在Owner 執行緒呼叫notify
或 notifyAll
時喚醒,但喚醒後並不意味者立刻獲得鎖,仍需進入 EntryList 重新競爭;
小南並不能直接進入WaitSet休息室,而是獲取鎖進入房間後才能進入休息室,沒有鎖的話小南沒法進入房間,更沒法進入休息室。他進入休息室後就會釋放鎖,讓其他執行緒競爭鎖進入房間。
public class Main { static final Object lock = new Object(); public static void main(String[] args) { synchronized (lock){ try { // 只有成為了monitor物件的owner,即獲得了物件鎖之後,才有資格進入waitset等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
小M此時獲取到了鎖,進入了房間,並喚醒了在休息室中等待的小王,小M如果獲取到鎖進行房間時沒有辦法喚醒在休息室等待的小王的,因為此時小M在門外,小王根本聽不到。
使用notify()喚醒等待區的一個執行緒:
public class Main { static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ System.out.println("t1執行緒開始執行..."); synchronized (lock){ try { // 讓t1執行緒在lock鎖的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 被喚醒後,繼續執行 System.out.println("執行緒t1被喚醒..."); } },"t1"); t1.start(); Thread t2 = new Thread(()->{ System.out.println("t2執行緒開始執行..."); synchronized (lock){ try { // 讓t2執行緒在lock鎖的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 被喚醒後,繼續執行 System.out.println("執行緒t2被喚醒..."); },"t2"); t2.start(); Thread.sleep(2000); System.out.println("喚醒lock鎖上等待的執行緒..."); synchronized (lock){ // 主執行緒拿到鎖後,喚醒正在休息室中等待的某一個執行緒 lock.notify(); } } }
執行結果:
t1執行緒開始執行...
t2執行緒開始執行...
喚醒lock鎖上等待的執行緒...
執行緒t1被喚醒...
使用notifyAll()喚醒等待區所有的執行緒:
public class Main { static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ System.out.println("t1執行緒開始執行..."); synchronized (lock){ try { // 讓t1執行緒在lock鎖的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 被喚醒後,繼續執行 System.out.println("執行緒t1被喚醒..."); } },"t1"); t1.start(); Thread t2 = new Thread(()->{ System.out.println("t2執行緒開始執行..."); synchronized (lock){ try { // 讓t2執行緒在lock鎖的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 被喚醒後,繼續執行 System.out.println("執行緒t2被喚醒..."); },"t2"); t2.start(); Thread.sleep(2000); System.out.println("喚醒lock鎖上等待的執行緒..."); synchronized (lock){ // 主執行緒拿到鎖後,喚醒正在休息室中等待的所有執行緒 lock.notifyAll(); } } }
執行結果:
t1執行緒開始執行...
t2執行緒開始執行...
喚醒lock鎖上等待的執行緒...
執行緒t2被喚醒...
執行緒t1被喚醒...
在呼叫同步物件的wait()和notify()系列方法時,“當前執行緒”必須擁有該物件的同步鎖,也就是說,wait()和notify()系列方法需要在同步塊中使用,否則JVM會丟擲類似如下的異常:
為什麼wait和notify不在synchronized同步塊的內部使用會丟擲異常呢?這需要從wait()和notify()方法的原理說起。
wait()方法的原理:
首先,JVM會釋放當前執行緒的物件鎖監視器的Owner資格;其次,JVM會將當前執行緒移入監視器的WaitSet佇列,而這些操作都和物件鎖監視器是相關的。所以,wait()方法必須在synchronized同步塊的內部呼叫。在當前執行緒執行wait()方法前,必須通過synchronized()方法成為物件鎖的監視器的Owner。
notify()方法的原理:
JVM從物件鎖的監視器的WaitSet佇列移動一個執行緒到其EntryList佇列,這些操作都與物件鎖的監視器有關。所以,notify()方法也必須在synchronized同步塊的內部呼叫。在執行notify()方法前,當前執行緒也必須通過synchronized()方法成為物件鎖的監視器的Owner。
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45