Java开发笔记(一百零三)线程间的通信方式
前面介紹了多線程并發(fā)之時的資源搶占情況,以及利用同步、加鎖、信號量等機制解決資源沖突問題,不過這些機制只適合同一資源的共享分配,并未涉及到某件事由的前因后果。日常生活中,經(jīng)常存在兩個前后關(guān)聯(lián)的事務(wù),像雇員和雇主這兩個角色,他們之間的某些工作就帶有因果關(guān)系。比如要等雇主接到了項目,雇員才有活干;又如每月末員工都等著老板發(fā)工資,這樣才有錢逛街和吃大餐,此時員工的消費行為便依賴于老板的發(fā)薪水動作。如此看來,兩個線程之間理應(yīng)建立某種消息通路,每當線程A完成某個事項,就將完成標志通知線程B,線程B收到通知之后,認為前提條件已經(jīng)滿足,這才進行后續(xù)的處理過程。線程之間的消息通路,可視作在線程間傳遞信息,專業(yè)的說法叫做“通信”,如何在多線程并發(fā)時進行有效通信,這是多線程技術(shù)中的一大課題。
依據(jù)線程并發(fā)時的不同管理機制,線程間的通信也各有不同的方式,接下來將分別論述同步機制與加鎖機制之下的兩種線程通信過程。
首先是同步機制,采用同步代碼塊的話,需要在關(guān)鍵字synchronized后面補充待同步的對象實例,之前的同步代碼塊統(tǒng)一寫成“synchronized (this)”??墒菆A括號內(nèi)部一定要填this嗎?圓括號的內(nèi)部參數(shù)究竟是干什么用的?其實synchronized附帶的圓括號參數(shù)正是在線程間通信的郵差,以前的同步演示代碼由于沒進行線程通信,因此圓括號里的參數(shù)沒有具體要求,一般填this即可?,F(xiàn)在要想在線程間進行通信,就必須啟用圓括號參數(shù)了,并且兩個線程都要在synchronized后面填寫該參數(shù)對象。
舉個例子,雇員等著雇主發(fā)工資,那員工怎樣才知道老板已經(jīng)發(fā)了呢?要是由員工自己一會兒一會兒去查銀行卡,平時的工作都會受到影響,所以可讓員工留個等工資的心眼就好。然后老板一個一個發(fā)工資,發(fā)完之后給員工遞個工資條,或者給員工發(fā)封工資郵件,這樣員工收到工資條便知薪水到賬了。那么在等工資和發(fā)工資這兩個線程之間,即可令工資條作為二者的信使,于是同步代碼塊可改寫為“synchronized (工資條對象)”的形式。同時工資條對象還要支持等待與發(fā)放兩個動作,因為這類動作早就隱藏在Object類的基本方法中,所以開發(fā)者不必擔(dān)心工資條對象該為Integer類型還是別的什么類型,凡是正常的實例都擁有等待與發(fā)放的方法,具體的方法說明如下:
wait:等待通知。
notify:在等待隊列中隨機挑選一個線程發(fā)放通知。
notifyAll:向等待隊列中的所有線程發(fā)放通知。
在編碼實現(xiàn)同步機制的通信過程時,先分別創(chuàng)建雇員和雇主的工作任務(wù),其中雇員任務(wù)在同步代碼塊中調(diào)用工資條對象的wait方法,表示等著發(fā)工資;而雇主任務(wù)在同步代碼塊中調(diào)用工資條對象的notify方法,表示發(fā)完工資了。然后依次啟動員工線程和老板線程,員工線程負責(zé)等工資以及收到工資后的消費行為,老板線程負責(zé)發(fā)工資以及記賬操作。據(jù)此編寫的同步線程通信代碼示例如下:
運行上面的線程通信代碼,打印出以下的線程日志:
14:37:29.685 同步機制的員工 等著發(fā)工資。 14:37:29.994 同步機制的老板 開始發(fā)工資。 14:37:30.120 同步機制的老板 發(fā)完工資了。 14:37:30.120 同步機制的員工 今晚趕緊吃大餐。?
從日志可見,員工線程果然在等到工資之后才去吃大餐。
同步機制能夠通過wait/notify完成線程通信功能,那么加鎖機制又該如何進行線程間通信呢?既然加鎖機制設(shè)計了專門的鎖工具,那么鎖鑰內(nèi)外的線程也只能通過鎖工具來通信,信使則為調(diào)用鎖對象的newCondition方法返回的Condition條件對象。條件對象同樣擁有等待與發(fā)放的方法,且與Object類的三個方法一一對應(yīng),具體說明如下:
await:等待通知。
signal:在等待隊列中隨機挑選一個線程發(fā)放通知。
signalAll:向等待隊列中的所有線程發(fā)放通知。
以可重入鎖ReentrantLock為例,依然要先分別創(chuàng)建雇員和雇主的工作任務(wù),其中雇員任務(wù)在加鎖之后再調(diào)用條件對象的await方法,表示等著發(fā)工資;而雇主任務(wù)在加鎖之后再調(diào)用條件對象的signal方法,表示發(fā)完工資了;另外雇員任務(wù)和雇主任務(wù)均需在結(jié)束之前進行解鎖。然后依次啟動員工線程和老板線程,員工線程負責(zé)等工資以及收到工資后的消費行為,老板線程負責(zé)發(fā)工資以及記賬操作。下面是在加解鎖線程之間進行通信的代碼例子:
?
運行上述的線程通信代碼,打印出如下的線程日志:
14:57:07.794 加鎖機制的員工 等著發(fā)工資。 14:57:07.801 加鎖機制的老板 開始發(fā)工資。 14:57:07.905 加鎖機制的老板 發(fā)完工資了。 14:57:07.906 加鎖機制的員工 今晚趕緊吃大餐。?
可見加鎖機制同樣實現(xiàn)了線程間通信的功能。
更多Java技術(shù)文章參見《Java開發(fā)筆記(序)章節(jié)目錄》
轉(zhuǎn)載于:https://www.cnblogs.com/pinlantu/p/10933647.html
總結(jié)
以上是生活随笔為你收集整理的Java开发笔记(一百零三)线程间的通信方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ Qt 访问权限总结
- 下一篇: 数学知识巧学JCF(Java Colle