亚洲全黄无码一级在线看_国产剧情久久久性色_无码av一区二区三区无码_亚洲成a×人片在线观看

當前位置: 首頁 > 科技新聞 >

為什么 wait 必須在 synchronized 保護的同步代碼中使

時間:2020-06-04 17:38來源:網(wǎng)絡整理 瀏覽:
作者簡介:徐隆曦,滴滴出行高級工程師。本文選自:拉勾教育專欄《Java 并發(fā)編程 78 講》你好,我是徐隆曦,今天我們主要探討一個問題?:?

作者簡介:徐隆曦,滴滴出行高級工程師。

本文選自:拉勾教育專欄《Java 并發(fā)編程 78 講》

你好,我是徐隆曦,今天我們主要探討一個問題?:??為什么 wait 必須在 synchronized 保護的同步代碼中使用?

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

首先,我們來看第一個問題,為什么 wait 方法必須在 synchronized 保護的同步代碼中使用?

我們先來看看 wait 方法的源碼注釋是怎么寫的:

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

上面這段英文的意思是說,在使用 wait 方法時,必須把 wait 方法寫在 synchronized 保護的 while 代碼塊中,并始終判斷執(zhí)行條件是否滿足,如果滿足就往下繼續(xù)執(zhí)行,如果不滿足就執(zhí)行 wait 方法,而在執(zhí)行 wait 方法之前,必須先持有對象的 monitor 鎖,也就是通常所說的 synchronized 鎖。那么設計成這樣有什么好處呢?

本文選自:拉勾教育專欄《Java 并發(fā)編程 78 講》見文末了解更多

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

我們逆向思考這個問題,如果不要求 wait 方法放在 synchronized 保護的同步代碼中使用,而是可以隨意調(diào)用,那么就有可能寫出這樣的代碼:

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

在代碼中可以看到有兩個方法,give 方法負責往 buffer 中添加數(shù)據(jù),添加完之后執(zhí)行 notify 方法來喚醒之前等待的線程,而 take 方法負責檢查整個 buffer 是否為空,如果為空就進入等待,如果不為空就取出一個數(shù)據(jù),這是典型的生產(chǎn)者消費者的思想。

本文選自:拉勾教育專欄《Java 并發(fā)編程 78 講》見文末了解更多

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

但是這段代碼并沒有受 synchronized 保護,于是便有可能發(fā)生以下場景:

首先,消費者線程調(diào)用 take 方法并判斷 buffer.isEmpty 方法是否返回 true,若為 true 代表buffer是空的,則線程希望進入等待,但是在線程調(diào)用 wait 方法之前,就被調(diào)度器暫停了,所以此時還沒來得及執(zhí)行 wait 方法。此時生產(chǎn)者開始運行,執(zhí)行了整個 give 方法,它往 buffer 中添加了數(shù)據(jù),并執(zhí)行了 notify 方法,但 notify 并沒有任何效果,因為消費者線程的 wait 方法沒來得及執(zhí)行,所以沒有線程在等待被喚醒。此時,剛才被調(diào)度器暫停的消費者線程回來繼續(xù)執(zhí)行 wait 方法并進入了等待。為什么 wait 必須在 synchronized 保護的同步代碼中使用?

雖然剛才消費者判斷了 buffer.isEmpty 條件,但真正執(zhí)行 wait 方法時,之前的buffer.isEmpty 的結果已經(jīng)過期了,不再符合最新的場景了,因為這里的“判斷-執(zhí)行”不是一個原子操作,它在中間被打斷了,是線程不安全的。

假設這時沒有更多的生產(chǎn)者進行生產(chǎn),消費者便有可能陷入無窮無盡的等待,因為它錯過了剛才 give 方法內(nèi)的 notify 的喚醒。

我們看到正是因為 wait 方法所在的 take 方法沒有被 synchronized 保護,所以它的 while 判斷和 wait 方法無法構成原子操作,那么此時整個程序就很容易出錯。

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

我們把代碼改寫成源碼注釋所要求的被 synchronized 保護的同步代碼塊的形式,代碼如下:

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

這樣就可以確保 notify 方法永遠不會在 buffer.isEmpty 和 wait 方法之間被調(diào)用,提升了程序的安全性。

另外,wait 方法會釋放monitor鎖,這也要求我們必須首先進入到 synchronized 內(nèi)持有這把鎖。

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

這里還存在一個“虛假喚醒”(spurious wakeup)的問題,線程可能在既沒有被notify/notifyAll,也沒有被中斷或者超時的情況下被喚醒,這種喚醒是我們不希望看到的。雖然在實際生產(chǎn)中,虛假喚醒發(fā)生的概率很小,但是程序依然需要保證在發(fā)生虛假喚醒的時候的正確性,所以就需要采用while循環(huán)的結構:

為什么 wait 必須在 synchronized 保護的同步代碼中使用?

這樣即便被虛假喚醒了,也會再次檢查while里面的條件,如果不滿足條件,就會繼續(xù)wait,也就消除了虛假喚醒的風險。

好了,本次的內(nèi)容講完了,下一課時我將講解“有哪幾種實現(xiàn)生產(chǎn)者-消費者模式的方法?”記得按時來學習哦,下次見。

本文選自:拉勾教育專欄《Java 并發(fā)編程 78 講》見文末了解更多

版權聲明:本文版權歸屬拉勾教育及該專欄作者,任何媒體、網(wǎng)站或個人未經(jīng)本網(wǎng)協(xié)議授權不得轉載、鏈接、轉貼或以其他方式復制發(fā)布/發(fā)表,違者必究。

推薦內(nèi)容