Java中wait()方法為什么要放在同步塊中佩研?

## 關于問題

我在工作的時候,有一位組員問題一個問題:如果wait()方法不放在同步代碼塊會怎樣旬薯?

我馬上要開會忙得不可開交绊序,只是回答了一句話:”規(guī)定“。

等到有時間了骤公,我仔細回顧下,如果wait()方法不在同步塊中凌节,代碼的確會拋出IllegalMonitorStateException:

```java

? ? @Test

? ? public void test() {

? ? ? ? try {

? ? ? ? ? ? new Object().wait();

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

```

![](https://upload-images.jianshu.io/upload_images/15590149-a35fb5ea3eb6890c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

但是洒试,為毛呢垒棋??為毛呢叼架?我也不知道啊碉碉,經過一番查閱,我找到了答案垢粮。

## Lost Wake-Up Problem

事情得從一個多線程編程里面臭名昭著的問題"Lost wake-up problem"說起。

這個問題并不是說只在Java語言中會出現(xiàn)毫蚓,而是會在所有的多線程環(huán)境下出現(xiàn)昔善。

假如我們有兩個線程,一個消費者線程翩概,一個生產者線程。生產者線程的任務可以簡化成先將count加一牍鞠,而后喚醒消費者评姨;消費者則是先將count減一,而后在減到0的時候陷入睡眠:

生產者偽代碼:

```swift

count+1;

notify();

```

消費者偽代碼:

```swift

while(count<=0)

? wait()

count--

```

這里面有問題胁后。什么問題呢嗦枢?

生產者是兩個步驟:

1. count+1;

2. notify();

消費者也是兩個步驟:

1. 檢查count值;

2. 睡眠或者減一敲才;

如果這些步驟混在一起會怎樣呢择葡?

比如說剃氧,初始的時候count等于0,這個時候消費者檢查count的值已添,發(fā)現(xiàn)count小于等于0的條件成立滥酥;就在這個時候,發(fā)生了上下文切換缆蝉,生產者進來了瘦真,噼噼啪啪一頓操作,把兩個步驟都執(zhí)行完了原杂,也就是發(fā)出了通知您机,準備喚醒一個線程年局。這個時候消費者剛決定睡覺咸产,還沒睡呢锐朴,所以這個通知就會被丟掉。緊接著焚志,消費者就睡過去了酱酬,消費者成睡美人了。

![](https://upload-images.jianshu.io/upload_images/15590149-cc87b208e0455b7a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這就是所謂的lost wake up問題汗菜。

## 嘗試解決

問題的根源在于挑社,消費者在檢查count到調用wait()之間,count就可能被改掉了痛阻。

常見的解決方式是加鎖阱当,讓消費者和生產者競爭一把鎖,競爭到了的弊添,才能夠修改count的值油坝。

我這里將兩者的兩個操作都放進去了同步塊中,于是生產者的代碼是:

```java

tryLock()

count+1

notify()

releaseLock()

```

消費者的代碼是:

```java

tryLock()

while(count <= 0)

? wait()


count-1

releaseLock()

```

但是這樣改后依舊會出現(xiàn)lost wake up問題彼水,而且和無鎖的表現(xiàn)是一樣的极舔。

## 最終解決

為了避免出現(xiàn)這種lost wake up問題,在這種模型之下盯桦,應該將我們的代碼放進去的同步塊中。

Java強制我們的wait()/notify()調用必須要在一個同步塊中贴膘,就是不想讓我們在不經意間出現(xiàn)這種lost wake up問題略号。

不僅僅是這兩個方法,包括java.util.concurrent.locks.Condition的await()/signal()也必須要在同步塊中:

```java

? ? private ReentrantLock lock = new ReentrantLock();

? ? private Condition condition = lock.newCondition();? ?

@Test

? ? public void test2() {

? ? ? ? try {

? ? ? ? ? ? condition.signal();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

```

![](https://upload-images.jianshu.io/upload_images/15590149-46fa9fca22143cbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

準確的來說突梦,即便是我們自己在實現(xiàn)自己的鎖機制的時候羽利,也應該要確保類似于wait()和notify()這種調用这弧,要在同步塊內,防止使用者出現(xiàn)lost wake up問題皇帮。

Java的這種檢測是很嚴格的户矢。它要求的是,一定要處于鎖對象的同步塊中梯浪。舉例來說:

```java

private Object obj = new Object();

? ? private Object anotherObj = new Object();

? ? @Test

? ? public void test3() {

? ? ? ? synchronized (obj) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? anotherObj.notify();

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

```

![](https://upload-images.jianshu.io/upload_images/15590149-8689188d7ef8e006.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這樣也是沒什么用的挂洛。一樣出現(xiàn)IllegalMonitorStateException眠砾。

## 提高面試技巧

假如面試官問你這個問題了褒颈,你不要一五一十的全部說出來。只需要輕描淡寫地說:“這是Java設計者為了避免使用者出現(xiàn)lost wake up問題而搞出來的”谷丸,其中的逼格大家自己去體會刨疼。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末鹅龄,一起剝皮案震驚了整個濱河市扮休,隨后出現(xiàn)的幾起案子拴鸵,更是在濱河造成了極大的恐慌,老刑警劉巖劲藐,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘩燥,死亡現(xiàn)場離奇詭異,居然都是意外死亡溶耘,警方通過查閱死者的電腦和手機服鹅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門企软,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人形庭,你說我怎么就攤上這事厌漂。” “怎么了苇倡?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵富纸,是天一觀的道長。 經常有香客問我旨椒,道長晓褪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任综慎,我火速辦了婚禮涣仿,結果婚禮上,老公的妹妹穿的比我還像新娘寥粹。我一直安慰自己变过,他們只是感情好埃元,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岛杀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崭孤。 梳的紋絲不亂的頭發(fā)上类嗤,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音辨宠,去河邊找鬼遗锣。 笑死,一個胖子當著我的面吹牛嗤形,可吹牛的內容都是我干的精偿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼赋兵,長吁一口氣:“原來是場噩夢啊……” “哼笔咽!你這毒婦竟也來了?” 一聲冷哼從身側響起霹期,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤叶组,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后历造,有當地人在樹林里發(fā)現(xiàn)了一具尸體甩十,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年吭产,在試婚紗的時候發(fā)現(xiàn)自己被綠了侣监。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡垮刹,死狀恐怖达吞,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情荒典,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布吞鸭,位于F島的核電站寺董,受9級特大地震影響,放射性物質發(fā)生泄漏刻剥。R本人自食惡果不足惜遮咖,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望造虏。 院中可真熱鬧御吞,春花似錦麦箍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揍诽,卻和暖如春诀蓉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暑脆。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工渠啤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人添吗。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓沥曹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親碟联。 傳聞我的和親對象是個殘疾皇子妓美,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容