日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java开发笔记(一百零三)线程间的通信方式

發布時間:2023/12/18 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java开发笔记(一百零三)线程间的通信方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面介紹了多線程并發之時的資源搶占情況,以及利用同步、加鎖、信號量等機制解決資源沖突問題,不過這些機制只適合同一資源的共享分配,并未涉及到某件事由的前因后果。日常生活中,經常存在兩個前后關聯的事務,像雇員和雇主這兩個角色,他們之間的某些工作就帶有因果關系。比如要等雇主接到了項目,雇員才有活干;又如每月末員工都等著老板發工資,這樣才有錢逛街和吃大餐,此時員工的消費行為便依賴于老板的發薪水動作。如此看來,兩個線程之間理應建立某種消息通路,每當線程A完成某個事項,就將完成標志通知線程B,線程B收到通知之后,認為前提條件已經滿足,這才進行后續的處理過程。線程之間的消息通路,可視作在線程間傳遞信息,專業的說法叫做“通信”,如何在多線程并發時進行有效通信,這是多線程技術中的一大課題。
依據線程并發時的不同管理機制,線程間的通信也各有不同的方式,接下來將分別論述同步機制與加鎖機制之下的兩種線程通信過程。
首先是同步機制,采用同步代碼塊的話,需要在關鍵字synchronized后面補充待同步的對象實例,之前的同步代碼塊統一寫成“synchronized (this)”。可是圓括號內部一定要填this嗎?圓括號的內部參數究竟是干什么用的?其實synchronized附帶的圓括號參數正是在線程間通信的郵差,以前的同步演示代碼由于沒進行線程通信,因此圓括號里的參數沒有具體要求,一般填this即可。現在要想在線程間進行通信,就必須啟用圓括號參數了,并且兩個線程都要在synchronized后面填寫該參數對象。
舉個例子,雇員等著雇主發工資,那員工怎樣才知道老板已經發了呢?要是由員工自己一會兒一會兒去查銀行卡,平時的工作都會受到影響,所以可讓員工留個等工資的心眼就好。然后老板一個一個發工資,發完之后給員工遞個工資條,或者給員工發封工資郵件,這樣員工收到工資條便知薪水到賬了。那么在等工資和發工資這兩個線程之間,即可令工資條作為二者的信使,于是同步代碼塊可改寫為“synchronized (工資條對象)”的形式。同時工資條對象還要支持等待與發放兩個動作,因為這類動作早就隱藏在Object類的基本方法中,所以開發者不必擔心工資條對象該為Integer類型還是別的什么類型,凡是正常的實例都擁有等待與發放的方法,具體的方法說明如下:
wait:等待通知。
notify:在等待隊列中隨機挑選一個線程發放通知。
notifyAll:向等待隊列中的所有線程發放通知。
在編碼實現同步機制的通信過程時,先分別創建雇員和雇主的工作任務,其中雇員任務在同步代碼塊中調用工資條對象的wait方法,表示等著發工資;而雇主任務在同步代碼塊中調用工資條對象的notify方法,表示發完工資了。然后依次啟動員工線程和老板線程,員工線程負責等工資以及收到工資后的消費行為,老板線程負責發工資以及記賬操作。據此編寫的同步線程通信代碼示例如下:

// 員工與老板之間通過工資條通信private static Integer salary = 5000;// 測試通過wait和notify方法進行線程間通信private static void testWaitNotify() {// 創建雇員的工作任務Runnable employee = new Runnable() {@Overridepublic void run() {PrintUtils.print(Thread.currentThread().getName(), "等著發工資。");synchronized (salary) { // 工資是我的,你們別搶try {salary.wait(); // 等待發工資// 打印拿到工資后的慶祝日志PrintUtils.print(Thread.currentThread().getName(), "今晚趕緊吃大餐。");} catch (InterruptedException e) { // 等待期間允許接收中斷信號e.printStackTrace();}}}};// 創建雇主的工作任務Runnable boss = new Runnable() {@Overridepublic void run() {// 稍等一會兒,老板線程的同步代碼塊務必在員工線程的同步代碼塊之后開始運行,否則員工線程將一直等待wait_a_moment();PrintUtils.print(Thread.currentThread().getName(), "開始發工資。");synchronized (salary) { // 由我發工資,你們別鬧wait_a_moment(); // 銀行轉賬也需要時間salary.notify(); // 隨機通知其中一個等待線程// 手好酸,發工資也是個體力活,記個賬PrintUtils.print(Thread.currentThread().getName(), "發完工資了。");}}};new Thread(employee, "同步機制的員工").start(); // 啟動員工等工資的線程new Thread(boss, "同步機制的老板").start(); // 啟動老板發工資的線程}// 稍等一會兒,模擬日常事務的時間消耗private static void wait_a_moment() {int delay = new Random().nextInt(500); // 生成500以內的隨機整數try {Thread.sleep(delay); // 睡眠若干毫秒} catch (InterruptedException e) {}}

運行上面的線程通信代碼,打印出以下的線程日志:

14:37:29.685 同步機制的員工 等著發工資。 14:37:29.994 同步機制的老板 開始發工資。 14:37:30.120 同步機制的老板 發完工資了。 14:37:30.120 同步機制的員工 今晚趕緊吃大餐。

?

從日志可見,員工線程果然在等到工資之后才去吃大餐。

同步機制能夠通過wait/notify完成線程通信功能,那么加鎖機制又該如何進行線程間通信呢?既然加鎖機制設計了專門的鎖工具,那么鎖鑰內外的線程也只能通過鎖工具來通信,信使則為調用鎖對象的newCondition方法返回的Condition條件對象。條件對象同樣擁有等待與發放的方法,且與Object類的三個方法一一對應,具體說明如下:
await:等待通知。
signal:在等待隊列中隨機挑選一個線程發放通知。
signalAll:向等待隊列中的所有線程發放通知。
以可重入鎖ReentrantLock為例,依然要先分別創建雇員和雇主的工作任務,其中雇員任務在加鎖之后再調用條件對象的await方法,表示等著發工資;而雇主任務在加鎖之后再調用條件對象的signal方法,表示發完工資了;另外雇員任務和雇主任務均需在結束之前進行解鎖。然后依次啟動員工線程和老板線程,員工線程負責等工資以及收到工資后的消費行為,老板線程負責發工資以及記賬操作。下面是在加解鎖線程之間進行通信的代碼例子:

// 創建一個可重入鎖private final static ReentrantLock reentrantLock = new ReentrantLock();// 獲取可重入鎖的條件對象private static Condition condition = reentrantLock.newCondition();// 測試通過Condition對象進行線程間通信private static void testCondition() {// 創建雇員的工作任務Runnable employee = new Runnable() {@Overridepublic void run() {PrintUtils.print(Thread.currentThread().getName(), "等著發工資。");reentrantLock.lock(); // 對可重入鎖加鎖try {condition.await(); // 這里在等待條件對象的信號// 打印拿到工資后的慶祝日志PrintUtils.print(Thread.currentThread().getName(), "今晚趕緊吃大餐。");} catch (InterruptedException e) { // 等待期間允許接收中斷信號e.printStackTrace();}reentrantLock.unlock(); // 對可重入鎖解鎖}};// 創建雇主的工作任務Runnable boss = new Runnable() {@Overridepublic void run() {// 稍等一會兒,老板線程的加鎖務必在員工線程的加鎖之后執行,否則員工線程將一直等待wait_a_moment();PrintUtils.print(Thread.currentThread().getName(), "開始發工資。");reentrantLock.lock(); // 對可重入鎖加鎖wait_a_moment(); // 銀行轉賬也需要時間condition.signal(); // 給條件對象發送信號// 手好酸,發工資也是個體力活,記個賬PrintUtils.print(Thread.currentThread().getName(), "發完工資了。");reentrantLock.unlock(); // 對可重入鎖解鎖}};new Thread(employee, "加鎖機制的員工").start(); // 啟動員工等工資的線程new Thread(boss, "加鎖機制的老板").start(); // 啟動老板發工資的線程}

?

運行上述的線程通信代碼,打印出如下的線程日志:

14:57:07.794 加鎖機制的員工 等著發工資。 14:57:07.801 加鎖機制的老板 開始發工資。 14:57:07.905 加鎖機制的老板 發完工資了。 14:57:07.906 加鎖機制的員工 今晚趕緊吃大餐。

?

可見加鎖機制同樣實現了線程間通信的功能。



更多Java技術文章參見《Java開發筆記(序)章節目錄》

轉載于:https://www.cnblogs.com/pinlantu/p/10933647.html

總結

以上是生活随笔為你收集整理的Java开发笔记(一百零三)线程间的通信方式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。