java線程間通訊的一些方法總結(jié)
前言
并發(fā)編程中,我們可能會遇到這樣一個場景
A、B兩個線程并行,但是我希望保證B線程在A線程執(zhí)行完了后再執(zhí)行
這個時候就需要線程間進行通訊
A執(zhí)行完了后對B說一聲,喂B,我執(zhí)行完了
來康康用Java怎么實現(xiàn)
1、基于synchronized
2、基于reentrantLock
3、基于volatile
4、基于countDownLatch
我目前就知道這四種
1、synchronized+wait() 和 notify()
wait() 和 notify()都是Object類的通訊方法,注意一點,wait和 notify必須搭配synchronized使用,并且wait()會釋放鎖,notify()不會釋放鎖
public class SynchronizedTest { //定義個year,用來記錄某明星的練習(xí)年數(shù) private static double year; public void run() { //線程A,練習(xí)唱跳rap Thread threadA = new Thread(() -> { synchronized (this) { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐雞開始練習(xí)唱跳rap:已練習(xí)" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //眾所周知,練習(xí)兩年半即可出道 if (year == 2.5) { System.out.println("===========================>成功練習(xí)兩年半,出道?。。?); this.notify(); } } } }); //線程B,練習(xí)打籃球 Thread threadB = new Thread(() -> { while (true) { synchronized (this) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐雞開始練習(xí)打籃球"); } } }); //注意,一定要先啟動B,不然會導(dǎo)致B永遠拿不到鎖 threadB.start(); threadA.start(); } public static void main(String[] args) { SynchronizedTest test = new SynchronizedTest(); test.run(); } }
運行結(jié)果:
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)0.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.5年
===========================>成功練習(xí)兩年半,出道?。?!
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)5.0年
蔡徐雞開始練習(xí)打籃球
注意看運行結(jié)果,線程A在執(zhí)行notify后并沒有釋放鎖,而是執(zhí)行完當(dāng)前任務(wù)才開始執(zhí)行線程B的任務(wù)
2、基于ReentrantLock
ReentrantLock也能實現(xiàn)線程間通訊,不過有點麻煩,需要結(jié)合ReentrantLock的Condition
public class LockTest { //定義個year,用來記錄某明星練習(xí)打籃球的年數(shù) private static double year; public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //線程A,練習(xí)唱跳rap Thread threadA = new Thread(() -> { //執(zhí)行業(yè)務(wù)代碼前上鎖 lock.lock(); for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐雞開始練習(xí)唱跳rap:已練習(xí)" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //眾所周知,練習(xí)兩年半即可出道 if (year == 2.5) { System.out.println("===========================>成功練習(xí)兩年半,出道?。?!"); //喚醒等待中的線程 condition.signal(); } } //業(yè)務(wù)代碼執(zhí)行完后解鎖 lock.unlock(); }); //線程B,練習(xí)打籃球 Thread threadB = new Thread(() -> { //執(zhí)行業(yè)務(wù)代碼前上鎖 lock.lock(); while (true) { try { //讓線程等待,如果計數(shù)器為0的話,則立即執(zhí)行 condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐老母雞開始練習(xí)打籃球"); break; } //業(yè)務(wù)代碼執(zhí)行完后解鎖 lock.unlock(); }); //注意,一定要先啟動B,不然會導(dǎo)致B永遠拿不到鎖 threadB.start(); threadA.start(); } }
運行結(jié)果:
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)0.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.5年
===========================>成功練習(xí)兩年半,出道!?。?br /> 蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)5.0年
蔡徐老母雞開始練習(xí)打籃球
效果和synchronized+wait() 和 notify()一樣一樣的
3、基于volatile
使用共享變量也能實現(xiàn),用volatile即可,原理就是多個線程共同監(jiān)聽同個變量,根據(jù)變量的值變化來執(zhí)行對應(yīng)的任務(wù),此處volatile的作用就是讓其它線程能即時感知變量值的改變
public class volatileTest { //定義一個共享變量,注意,必須用volatile修飾 static volatile boolean flag = false; //定義個year,用來記錄某明星練習(xí)打籃球的年數(shù) private static double year; public static void main(String[] args) { //線程A,練習(xí)唱跳rap Thread threadA = new Thread(() -> { while (true) { if (!flag) { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐雞開始練習(xí)唱跳rap:已練習(xí)" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //眾所周知,練習(xí)兩年半即可出道 if (year == 2.5) { System.out.println("===========================>成功練習(xí)兩年半,出道?。?!"); year = 0.5; flag = true; break; } } } } }); //線程B,練習(xí)打籃球 Thread threadB = new Thread(() -> { while (true) { if (flag) { System.out.println("蔡徐老母雞開始練習(xí)打籃球"); break; } } }); // 啟動線程 threadA.start(); threadB.start(); } }
運行結(jié)果:
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)0.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.5年
===========================>成功練習(xí)兩年半,出道?。。?br /> 蔡徐老母雞開始練習(xí)打籃球
基于CountDownLatch
CountDownLatch是JUC包下的一個并發(fā)編程工具,主要有兩個方法,countDown和await,CountDownLatch底層維護了一個計數(shù)器,在實例化的時候設(shè)置,當(dāng)調(diào)用countDown方法時,計數(shù)器減一,如果計數(shù)器在減一前已經(jīng)為0,那么什么都不會發(fā)生,如果減一后變成0,則喚醒所有等待的線程;await方法會使當(dāng)前線程等待,直到計數(shù)器為0
public class CountDownLatchTest { //定義個year,用來記錄某明星練習(xí)打籃球的年數(shù) private static double year; public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(1); //線程A,練習(xí)唱跳rap Thread threadA = new Thread(() -> { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐雞開始練習(xí)唱跳rap:已練習(xí)" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //眾所周知,練習(xí)兩年半即可出道 if (year == 2.5) { System.out.println("===========================>成功練習(xí)兩年半,出道?。?!"); //計數(shù)器減一 latch.countDown(); } } }); //線程B,練習(xí)打籃球 Thread threadB = new Thread(() -> { while (true) { try { //讓線程等待,如果計數(shù)器為0的話,則立即執(zhí)行 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐老母雞開始練習(xí)打籃球"); break; } }); // 啟動線程 threadA.start(); threadB.start(); } }
運行結(jié)果:
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)0.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)1.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)2.5年
===========================>成功練習(xí)兩年半,出道!?。?br /> 蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.0年
蔡徐老母雞開始練習(xí)打籃球
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)5.0年
如果你多運行幾次,你會發(fā)現(xiàn)線程B執(zhí)行的時機是隨機的,但永遠在計數(shù)器為0后才開始執(zhí)行,也就是說計數(shù)器為0后,線程A和線程B誰搶到鎖就誰執(zhí)行
文中所有demo都是復(fù)制即可運行,大家還是要多動手,家里有條件的都用idea跑一跑,沒條件的可以用手抄
總結(jié)
到此這篇關(guān)于java線程間通訊的文章就介紹到這了,更多相關(guān)java線程間通訊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Socket+JDBC+IO實現(xiàn)Java文件上傳下載器DEMO詳解
這篇文章主要介紹了Socket+JDBC+IO實現(xiàn)Java文件上傳下載器DEMO詳解,需要的朋友可以參考下2017-05-05Spring?Lifecycle?和?SmartLifecycle區(qū)別面試精講
這篇文章主要為大家介紹了Spring?Lifecycle和SmartLifecycle的區(qū)別面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10java數(shù)據(jù)結(jié)構(gòu)和算法中哈希表知識點詳解
在本篇文章里小編給大家分享了關(guān)于java數(shù)據(jù)結(jié)構(gòu)和算法中哈希表的相關(guān)知識點內(nèi)容,需要的朋友們學(xué)習(xí)下。2019-06-06datax-web在windows環(huán)境idea中模塊化打包部署操作步驟
這篇文章主要介紹了datax-web在windows環(huán)境idea中模塊化打包部署操作步驟,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05JavaWeb中Session對象的學(xué)習(xí)筆記
在WEB開發(fā)中,服務(wù)器可以為每個用戶瀏覽器創(chuàng)建一個會話對象,即session對象,這篇文章就為大家詳細介紹Session對象的定義、實現(xiàn)原理等基礎(chǔ)知識點,感興趣的小伙伴們可以參考一下2016-05-05java中List、Array、Map、Set等集合相互轉(zhuǎn)換
這篇文章主要介紹了java中List、Array、Map、Set等集合相互轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2017-05-05Java內(nèi)存溢出的幾個區(qū)域總結(jié)(注意避坑!)
內(nèi)存溢出是指應(yīng)用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多,最終使得程序運行要用到的內(nèi)存大于虛擬機能提供的最大內(nèi)存,下面這篇文章主要給大家介紹了關(guān)于Java內(nèi)存溢出的幾個區(qū)域,總結(jié)出來給大家提醒注意避坑,需要的朋友可以參考下2022-11-11一文詳解如何使用線程池來優(yōu)化我們的應(yīng)用程序
線程池是一種工具,但并不是適用于所有場景。在使用線程池時,我們需要根據(jù)應(yīng)用程序的性質(zhì)、計算資源的可用性和應(yīng)用程序的需求進行適當(dāng)?shù)呐渲?。本文主要介紹了如何使用線程池來優(yōu)化我們的應(yīng)用程序,需要的可以參考一下2023-04-04如何使用Spring Security手動驗證用戶的方法示例
這篇文章主要介紹了如何使用Spring Security手動驗證用戶的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05