一、概述
在前面兩篇文章當(dāng)中,我們介紹了synchronized
的基本使用和原理,但是在使用synchronized
保證數(shù)據(jù)一致性的同時澜沟,我們希望能夠讓線程之間進(jìn)行一些交互邏輯,也是我們今天要介紹的等待/通知模型峡谊,那么就需要使用到wait/notify
茫虽。
二、等待/通知相關(guān)方法
2.1 方法說明
下面既们,我們先介紹等待/通知機(jī)制的相關(guān)方法濒析,首先要說明兩點:
- 這些都是
Object
定義的方法 - 調(diào)用這些方法的前提條件是:該線程已經(jīng)獲得了
Object
對象所關(guān)聯(lián)的鎖,也就是說它們需要位于synchronized
修飾的同步代碼塊中贤壁。
**(a) wait() **
調(diào)用該方法的線程進(jìn)入等待狀態(tài),并釋放它所獲取的對象鎖埠忘,只有出現(xiàn)這兩種情況之一脾拆,它才會從wait
方法中返回,否則將會一直處于等待狀態(tài):
- 其它線程通過
notify / notifyAll
方法通知該線程莹妒,并且該線程獲取到了對象鎖 - 線程被中斷
(b) wait(long) / wait(long, int)
和wait
方法相同名船,差別是增加一種從wait
方法返回的情況:等待的時間已經(jīng)到了,并且獲取到了對象鎖旨怠。
(c) notify()
通知位于等待隊列中的第一個線程渠驼,使其從wait()
方法返回,而被通知的線程的繼續(xù)執(zhí)行需要等到它獲得對象所為止鉴腻。
需要注意迷扇,調(diào)用notify
方法后百揭,并不會立刻釋放它所持有的對象鎖,這需要等到它執(zhí)行完同步代碼塊為止蜓席。
(d) notifyAll()
與notify()
類似器一,但是它是通知所有在對象上等待的線程。
2.2 實現(xiàn)原理
通過上面的介紹厨内,我們可以看到祈秕,在整個等待/通知機(jī)制當(dāng)中,線程被掛起時主要有以下三種狀態(tài):等待狀態(tài)雏胃、超時等待狀態(tài)请毛、阻塞狀態(tài),這些狀態(tài)都是通過synchroized
所修飾的對象來實現(xiàn)的瞭亮。
在前面我們介紹synchronized
原理的時候方仿,曾經(jīng)說過每個對象都會和一個Monitor
相關(guān)聯(lián),其實每個Monitor
又包含有兩個隊列:等待隊列和同步隊列街州,其中等待隊列中存放是進(jìn)入等待狀態(tài)的線程兼丰,而同步隊列中存放的是等待獲取鎖的線程。
下面唆缴,我們通過一段簡單的偽代碼來立即兩個線程的狀態(tài)轉(zhuǎn)換過程:
synchronized public void waitThread() {
//執(zhí)行a方法.
wait();
//執(zhí)行b方法
}
synchronized public void notifyThread() {
//執(zhí)行c方法
notify();
//執(zhí)行d方法
}
我們有AB
兩個線程鳍征,我們模擬以下的一系列行為:
(1) A 線程執(zhí)行 waitThread 方法
此時由于對象鎖沒有被任何線程持有,因此面徽,A
線程成為對象鎖的持有者:
(2) B 線程執(zhí)行 notifyThread 方法
當(dāng)
B
線程執(zhí)行notifyThread
方法時艳丛,由于此時對象鎖已經(jīng)被A
線程持有,因此它被加入到同步隊列中:(3) A 線程執(zhí)行 a 方法
**(4) A 線程執(zhí)行 wait 方法 **
當(dāng)
A
線程執(zhí)行wait
方法后趟紊,它會釋放對象鎖氮双,并加入到等待隊列當(dāng)中,而B
線程則成為對象鎖新的持有者:(5) B 線程執(zhí)行 c 方法
(6) B 線程執(zhí)行 notify 方法
此時會喚醒等待隊列中
A
線程霎匈,但是此時B
線程仍然持有對象鎖戴差,因此,A
線程只能被加入到同步隊列:(7) B 線程執(zhí)行 d 方法
(8) B 線程從 notifyThread 方法返回
此時
A
線程重新獲取到對象鎖铛嘱,因此它被從同步隊列中取出暖释,繼續(xù)執(zhí)行接下來的邏輯:(9) A 線程執(zhí)行 b 方法
(10) A 線程從 waitThread 方法中返回
當(dāng)
A
線程從同步方法返回之后,那么會釋放它所持有的鎖三墨吓、等待/通知的經(jīng)典范式
對于等待/通知模型球匕,我們可以總結(jié)出它的經(jīng)典范式,分別針對等待方和通知方帖烘。
3.1 等待方
等待方遵循如下的原則:
- 獲取對象的鎖
- 如果條件不滿足亮曹,那么調(diào)用對象的
wait
方法,被通知后仍然需要檢查條件 - 條件滿足則繼續(xù)執(zhí)行對應(yīng)的邏輯
對應(yīng)的偽代碼為:
synchronized( 對象 ) {
while( 條件不滿足 ) {
對象.wait();
}
對應(yīng)的處理邏輯
}
3.2 通知方
通知方遵循如下的原則:
- 獲得對象的鎖
- 改變條件
- 通知所有等待在對象上的線程
synchronized( 對象 ) {
改變條件;
對象.notifyAll();
}