【并發(fā)技術(shù)05】傳統(tǒng)線程同步通信技術(shù)
我們先來看一個(gè)問題:
有兩個(gè)線程,子線程先執(zhí)行10次,然后主線程執(zhí)行5次,然后再切換到子線程執(zhí)行10,再主線程執(zhí)行5次……如此往返執(zhí)行50次。
看完這個(gè)問題,很明顯要用到線程間的通信了, 先分析一下思路:首先肯定要有兩個(gè)線程,然后每個(gè)線程中肯定有個(gè)50次的循環(huán),因?yàn)槊總€(gè)線程都要往返執(zhí)行任務(wù)50次,主線程的任務(wù)是執(zhí)行5次,子線程的任務(wù)是執(zhí)行10次。線程間通信技術(shù)主要用到?wait()?方法和?notify()?方法。?wait()?方法會(huì)導(dǎo)致當(dāng)前線程等待,并釋放所持有的鎖,?notify()?方法表示喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程。下面來一步步完成這道線程間通信問題。
首先不考慮主線程和子線程之間的通信,先把各個(gè)線程所要執(zhí)行的任務(wù)寫好:
public?class?TraditionalThreadCommunication?{????public?static?void?main(String[]?args)?{????????//開啟一個(gè)子線程????????new?Thread(new?Runnable()?{?????????????????????@Override????????????public?void?run()?{????????????????for(int?i?=?1;?i?<=?50;?i?++)?{?????????????????????????????????????synchronized?(TraditionalThreadCommunication.class)?{????????????????????????//子線程任務(wù):執(zhí)行10次???????????????????????????????????????for(int?j?=?1;j?<=?10;?j?++)?{????????????????????????????System.out.println("sub?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????????????????????}???????????????????????}????????????????}????????????}????????}).start();????????//main方法即主線程????????for(int?i?=?1;?i?<=?50;?i?++)?{????????????synchronized?(TraditionalThreadCommunication.class)?{????????????????//主線程任務(wù):執(zhí)行5次????????????????for(int?j?=?1;j?<=?5;?j?++)?{????????????????????System.out.println("main?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????????????}???????????????}???????????????}????}}
如上,兩個(gè)線程各有50次大循環(huán),執(zhí)行50次任務(wù),子線程的任務(wù)是執(zhí)行10次,主線程的任務(wù)是執(zhí)行5次。為了保證兩個(gè)線程間的同步問題,所以用了 synchronized 同步代碼塊,并使用了相同的鎖:類的字節(jié)碼對(duì)象。這樣可以保證線程安全。但是這種設(shè)計(jì)不太好,就像我在上一節(jié)的死鎖中寫的一樣,我們可以把線程任務(wù)放到一個(gè)類中,這種設(shè)計(jì)的模式更加結(jié)構(gòu)化,而且把不同的線程任務(wù)放到同一個(gè)類中會(huì)很容易解決同步問題,因?yàn)樵谝粋€(gè)類中很容易使用同一把鎖。所以把上面的程序修改一下:
public?class?TraditionalThreadCommunication?{????public?static?void?main(String[]?args)?{????????Business?bussiness?=?new?Business();?//new一個(gè)線程任務(wù)處理類????????//開啟一個(gè)子線程????????new?Thread(new?Runnable()?{?????????????????????@Override????????????public?void?run()?{????????????????for(int?i?=?1;?i?<=?50;?i?++)?{????????????????????bussiness.sub(i);????????????????}???????????????????????????}????????}).start();????????//main方法即主線程????????for(int?i?=?1;?i?<=?50;?i?++)?{????????????bussiness.main(i);????????}????}}//要用到的共同數(shù)據(jù)(包括同步鎖)或共同的若干個(gè)方法應(yīng)該歸在同一個(gè)類身上,這種設(shè)計(jì)正好體現(xiàn)了高類聚和程序的健壯性。class?Business?{????public?synchronized?void?sub(int?i)?{????????for(int?j?=?1;j?<=?10;?j?++)?{????????????System.out.println("sub?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????}???????}???????public?synchronized?void?main(int?i)?{????????for(int?j?=?1;j?<=?5;?j?++)?{????????????System.out.println("main?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????}}
經(jīng)過這樣修改后,程序結(jié)構(gòu)更加清晰了,也更加健壯了,只要在兩個(gè)線程任務(wù)方法上加上 synchronized 關(guān)鍵字即可,用的都是 this 這把鎖。但是現(xiàn)在兩個(gè)線程之間還沒有通信,執(zhí)行的結(jié)果是主線程循環(huán)執(zhí)行任務(wù)50次,然后子線程再循環(huán)執(zhí)行任務(wù)50次,原因很簡(jiǎn)單,因?yàn)橛?synchronized 同步。
下面繼續(xù)完善程序,讓兩個(gè)線程之間完成題目中所描述的那樣通信:
public?class?TraditionalThreadCommunication?{????public?static?void?main(String[]?args)?{????????Business?bussiness?=?new?Business();?//new一個(gè)線程任務(wù)處理類????????//開啟一個(gè)子線程????????new?Thread(new?Runnable()?{?????????????????????@Override????????????public?void?run()?{????????????????for(int?i?=?1;?i?<=?50;?i?++)?{????????????????????bussiness.sub(i);????????????????}???????????????????????????}????????}).start();????????//main方法即主線程????????for(int?i?=?1;?i?<=?50;?i?++)?{????????????bussiness.main(i);????????}????}}//要用到共同數(shù)據(jù)(包括同步鎖)或共同的若干個(gè)方法應(yīng)該歸在同一個(gè)類身上,這種設(shè)計(jì)正好體現(xiàn)了高雷劇和程序的健壯性。class?Business?{????private?boolean?bShouldSub?=?true;??????public?synchronized?void?sub(int?i)?{????????while(!bShouldSub)?{?//如果不輪到自己執(zhí)行,就睡????????????try?{????????????????this.wait();?//調(diào)用wait()方法的對(duì)象必須和synchronized鎖對(duì)象一致,這里synchronized在方法上,所以用this????????????}?catch?(InterruptedException?e)?{????????????????//?TODO?Auto-generated?catch?block????????????????e.printStackTrace();????????????}????????}????????for(int?j?=?1;j?<=?10;?j?++)?{????????????System.out.println("sub?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????}???????????bShouldSub?=?false;?//改變標(biāo)記????????this.notify();?//喚醒正在等待的主線程????}????public?synchronized?void?main(int?i)?{????????while(bShouldSub)?{?//如果不輪到自己執(zhí)行,就睡????????????try?{????????????????this.wait();????????????}?catch?(InterruptedException?e)?{????????????????//?TODO?Auto-generated?catch?block????????????????e.printStackTrace();????????????}????????}????????for(int?j?=?1;j?<=?5;?j?++)?{????????????System.out.println("main?thread?sequence?of?"?+?j?+?",?loop?of?"?+?i);????????}????????bShouldSub?=?true;?//改變標(biāo)記????????this.notify();?//喚醒正在等待的子線程????}}
首先,先不說具體的程序?qū)崿F(xiàn),就從結(jié)構(gòu)上來看,已經(jīng)體會(huì)到了這種設(shè)計(jì)的好處了:主函數(shù)里不用修改任何東西,關(guān)于線程間同步和線程間通信的邏輯全都在 Business 類中,主函數(shù)中的不同線程只需要調(diào)用放在該類中對(duì)應(yīng)的任務(wù)即可。體現(xiàn)了高類聚的好處。
再看一下具體的代碼,首先定義一個(gè) boolean 型變量來標(biāo)識(shí)哪個(gè)線程該執(zhí)行,當(dāng)不是子線程執(zhí)行的時(shí)候,它就睡,那么很自然主線程就執(zhí)行了,執(zhí)行完了,修改了 bShouldSub 并喚醒了子線程,子線程這時(shí)候再判斷一下 while 不滿足了,就不睡了,就執(zhí)行子線程任務(wù),同樣地,剛剛主線程修改了 bShouldSub 后,第二次循環(huán)來執(zhí)行主線程任務(wù)的時(shí)候,判斷 while 滿足就睡了,等待子線程來喚醒。這樣邏輯就很清楚了,主線程和子線程你一下我一下輪流執(zhí)行各自的任務(wù),這種節(jié)奏共循環(huán)50次。
另外有個(gè)小小的說明:這里其實(shí)用 if 來判斷也是可以的,但是為什么要用 while 呢?因?yàn)橛袝r(shí)候線程會(huì)假醒(就好像人的夢(mèng)游,明明正在睡,結(jié)果站起來了),如果用的是 if 的話,那么它假醒了后,就不會(huì)再返回去判斷 if 了,那它就很自然的往下執(zhí)行任務(wù),好了,另一個(gè)線程正在執(zhí)行呢,啪嘰一下就與另一個(gè)線程之間相互影響了。但是如果是 while 的話就不一樣了,就算線程假醒了,它還會(huì)判斷一下 while 的,但是此時(shí)另一個(gè)線程在執(zhí)行啊,bShouldSub 并沒有被修改,所以還是進(jìn)到 while 里了,又被睡了~所以很安全,不會(huì)影響另一個(gè)線程!官方 JDK 文檔中也是這么干的。
如果覺得對(duì)您有幫助,請(qǐng)轉(zhuǎn)發(fā)給更多人吧~
任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。