面試官:為什么 wait() 方法需要寫在while里、而不是if? 我回答不上來
點(diǎn)擊上方“碼農(nóng)突圍”,馬上關(guān)注
這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包
真愛,請?jiān)O(shè)置“星標(biāo)”或點(diǎn)個“在看”

問:為什么是 while 而不是 if ?
synchronized?(obj)?{
?????while?(check?pass)?{
????????wait();
????}
????//?do?your?business
}
static?class?Buf?{
????private?final?int?MAX?=?5;
????private?final?ArrayList?list?=?new?ArrayList<>();
????synchronized?void?put(int?v)?throws?InterruptedException?{
????????if?(list.size()?==?MAX)?{
????????????wait();
????????}
????????list.add(v);
????????notifyAll();
????}
????synchronized?int?get()?throws?InterruptedException?{
????????//?line?0?
????????if?(list.size()?==?0)?{??//?line?1
????????????wait();??//?line2
????????????//?line?3
????????}
????????int?v?=?list.remove(0);??//?line?4
????????notifyAll();?//?line?5
????????return?v;
????}
????synchronized?int?size()?{
????????return?list.size();
????}
}
下面的代碼用了 1 個線程來 put,10 個線程來 get:
final?Buf?buf?=?new?Buf();
ExecutorService?es?=?Executors.newFixedThreadPool(11);
for?(int?i?=?0;?i?1;?i++)
es.execute(new?Runnable()?{
????@Override
????public?void?run()?{
????????while?(true?)?{
????????????try?{
????????????????buf.put(1);
????????????????Thread.sleep(20);
????????????}
????????????catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????????break;
????????????}
????????}
????}
});
for?(int?i?=?0;?i?10;?i++)?{
????es.execute(new?Runnable()?{
????????@Override
????????public?void?run()?{
????????????while?(true?)?{
????????????????try?{
????????????????????buf.get();
????????????????????Thread.sleep(10);
????????????????}
????????????????catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????????break;
????????????????}
????????????}
????????}
????});
}
es.shutdown();
es.awaitTermination(1,?TimeUnit.DAYS);
java.lang.IndexOutOfBoundsException:?Index:?0,?Size:?0
at?java.util.ArrayList.rangeCheck(ArrayList.java:653)?
at?java.util.ArrayList.remove(ArrayList.java:492)?
at?TestWhileWaitBuf.get(TestWhileWait.java:80)atTestWhileWait2.run(TestWhileWait.java:47)?
at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)?
at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)?
at?java.lang.Thread.run(Thread.java:745)
synchronized?int?get()?throws?InterruptedException?{
??????while?(list.size()?==?0)?{
??????????wait();
??????}
??????int?v?=?list.remove(0);
??????notifyAll();
??????return?v;
??}
final?Buf?buf?=?new?Buf();
ExecutorService?es?=?Executors.newFixedThreadPool(11);
ScheduledExecutorService?printer?=?Executors.newScheduledThreadPool(1);
printer.scheduleAtFixedRate(new?Runnable()?{
????@Override
????public?void?run()?{
????????System.out.println(buf.size());
????}
},?0,?1,?TimeUnit.SECONDS);
for?(int?i?=?0;?i?10;?i++)
es.execute(new?Runnable()?{
????@Override
????public?void?run()?{
????????while?(true?)?{
????????????try?{
????????????????buf.put(1);
????????????????Thread.sleep(200);
????????????}
????????????catch?(InterruptedException?e)?{
?????????????????e.printStackTrace();
????????????????break;
????????????}
????????}
????}
});
for?(int?i?=?0;?i?1;?i++)?{
????es.execute(new?Runnable()?{
????????@Override
????????public?void?run()?{
????????????while?(true?)?{
????????????????try?{
????????????????????buf.get();
????????????????????Thread.sleep(100);
????????????????}
????????????????catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????????break;
????????????????}
????????????}
????????}
????});
}
es.shutdown();
es.awaitTermination(1,?TimeUnit.DAYS);
問:什么時候用 notifyAll 或者 notify?
synchronized?void?put(int?v)?throws?InterruptedException?{
???????if?(list.size()?==?MAX)?{
???????????wait();
???????}
???????list.add(v);
???????notify();
???}
???synchronized?int?get()?throws?InterruptedException?{
???????while?(list.size()?==?0)?{
???????????wait();
???????}
???????int?v?=?list.remove(0);
???????notify();
???????return?v;
???}
任何時候,被喚醒的來執(zhí)行的線程是不可預(yù)知。比如有 5 個線程都在一個對象上,實(shí)際上我不知道 下一個哪個線程會被執(zhí)行。
synchronized 語義實(shí)現(xiàn)了有且只有一個線程可以執(zhí)行同步塊里面的代碼。
C – 消費(fèi)者 調(diào)用 get。
如果 C1 把 C2 喚醒了,所以P2 (其他的都得等)只能在put方法上等著。(等待獲取synchoronized (this) 這個monitor)。
C2 檢查 while 循環(huán)發(fā)現(xiàn)此時隊(duì)列是空的,所以就在 wait 里面等著。
C3 也比 P2 先執(zhí)行,那么發(fā)現(xiàn)也是空的,只能等著了。
最近熱文
? ?太優(yōu)秀了!26歲當(dāng)上985博導(dǎo),這才是乘風(fēng)破浪的姐姐! ???fastjson的作者,在阿里內(nèi)網(wǎng)被噴,這到底是怎么一回事? ???為什么有些大公司技術(shù)弱爆了? ???微軟最新聲明:若斷供中國Windows,概不負(fù)責(zé)! 最近整理了一份大廠算法刷題指南,包括一些刷題技巧,在知乎上已經(jīng)有上萬贊。同時還整理了一份6000頁面試筆記。關(guān)注下面公眾號,在公眾號內(nèi)回復(fù)「刷題」,即可免費(fèi)獲取!回復(fù)「加群」,可以邀請你加入讀者群!
明天見(??ω??)??
評論
圖片
表情
