java線程間通訊的一些方法總結(jié)
前言
并發(fā)編程中,我們可能會(huì)遇到這樣一個(gè)場(chǎng)景
A、B兩個(gè)線程并行,但是我希望保證B線程在A線程執(zhí)行完了后再執(zhí)行
這個(gè)時(shí)候就需要線程間進(jìn)行通訊
A執(zhí)行完了后對(duì)B說一聲,喂B,我執(zhí)行完了
來康康用Java怎么實(shí)現(xiàn)
1、基于synchronized
2、基于reentrantLock
3、基于volatile
4、基于countDownLatch
我目前就知道這四種
1、synchronized+wait() 和 notify()
wait() 和 notify()都是Object類的通訊方法,注意一點(diǎn),wait和 notify必須搭配synchronized使用,并且wait()會(huì)釋放鎖,notify()不會(huì)釋放鎖
public class SynchronizedTest {
//定義個(gè)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í)打籃球");
}
}
});
//注意,一定要先啟動(dòng)B,不然會(huì)導(dǎo)致B永遠(yuǎn)拿不到鎖
threadB.start();
threadA.start();
}
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
test.run();
}
}
運(yùn)行結(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í)打籃球
注意看運(yùn)行結(jié)果,線程A在執(zhí)行notify后并沒有釋放鎖,而是執(zhí)行完當(dāng)前任務(wù)才開始執(zhí)行線程B的任務(wù)
2、基于ReentrantLock
ReentrantLock也能實(shí)現(xiàn)線程間通訊,不過有點(diǎn)麻煩,需要結(jié)合ReentrantLock的Condition
public class LockTest {
//定義個(gè)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 {
//讓線程等待,如果計(jì)數(shù)器為0的話,則立即執(zhí)行
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("蔡徐老母雞開始練習(xí)打籃球");
break;
}
//業(yè)務(wù)代碼執(zhí)行完后解鎖
lock.unlock();
});
//注意,一定要先啟動(dòng)B,不然會(huì)導(dǎo)致B永遠(yuǎn)拿不到鎖
threadB.start();
threadA.start();
}
}
運(yùn)行結(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í)打籃球
效果和synchronized+wait() 和 notify()一樣一樣的
3、基于volatile
使用共享變量也能實(shí)現(xiàn),用volatile即可,原理就是多個(gè)線程共同監(jiān)聽同個(gè)變量,根據(jù)變量的值變化來執(zhí)行對(duì)應(yīng)的任務(wù),此處volatile的作用就是讓其它線程能即時(shí)感知變量值的改變
public class volatileTest {
//定義一個(gè)共享變量,注意,必須用volatile修飾
static volatile boolean flag = false;
//定義個(gè)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;
}
}
});
// 啟動(dòng)線程
threadA.start();
threadB.start();
}
}
運(yùn)行結(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í)打籃球
基于CountDownLatch
CountDownLatch是JUC包下的一個(gè)并發(fā)編程工具,主要有兩個(gè)方法,countDown和await,CountDownLatch底層維護(hù)了一個(gè)計(jì)數(shù)器,在實(shí)例化的時(shí)候設(shè)置,當(dāng)調(diào)用countDown方法時(shí),計(jì)數(shù)器減一,如果計(jì)數(shù)器在減一前已經(jīng)為0,那么什么都不會(huì)發(fā)生,如果減一后變成0,則喚醒所有等待的線程;await方法會(huì)使當(dāng)前線程等待,直到計(jì)數(shù)器為0
public class CountDownLatchTest {
//定義個(gè)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í)兩年半,出道?。?!");
//計(jì)數(shù)器減一
latch.countDown();
}
}
});
//線程B,練習(xí)打籃球
Thread threadB = new Thread(() -> {
while (true) {
try {
//讓線程等待,如果計(jì)數(shù)器為0的話,則立即執(zhí)行
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("蔡徐老母雞開始練習(xí)打籃球");
break;
}
});
// 啟動(dòng)線程
threadA.start();
threadB.start();
}
}
運(yùn)行結(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í)打籃球
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)3.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.0年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)4.5年
蔡徐雞開始練習(xí)唱跳rap:已練習(xí)5.0年
如果你多運(yùn)行幾次,你會(huì)發(fā)現(xiàn)線程B執(zhí)行的時(shí)機(jī)是隨機(jī)的,但永遠(yuǎn)在計(jì)數(shù)器為0后才開始執(zhí)行,也就是說計(jì)數(shù)器為0后,線程A和線程B誰搶到鎖就誰執(zhí)行
文中所有demo都是復(fù)制即可運(yùn)行,大家還是要多動(dòng)手,家里有條件的都用idea跑一跑,沒條件的可以用手抄
總結(jié)
到此這篇關(guān)于java線程間通訊的文章就介紹到這了,更多相關(guān)java線程間通訊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)排球比賽計(jì)分系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)排球比賽計(jì)分系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
java對(duì)接支付寶支付接口簡(jiǎn)單步驟記錄
最近項(xiàng)目APP需要接入微信、支付寶支付功能,在分配開發(fā)任務(wù)時(shí),聽說微信支付接口比支付寶支付接口要難實(shí)現(xiàn),這篇文章主要給大家介紹了關(guān)于java對(duì)接支付寶支付接口的簡(jiǎn)單步驟,需要的朋友可以參考下2024-05-05
Struts2源碼分析之ParametersInterceptor攔截器
這篇文章主要介紹了Struts2源碼分析之ParametersInterceptor攔截器,ParametersInterceptor攔截器其主要功能是把ActionContext中的請(qǐng)求參數(shù)設(shè)置到ValueStack中,,需要的朋友可以參考下2019-06-06
java WSDL接口webService實(shí)現(xiàn)方式
這篇文章主要為大家詳細(xì)介紹了java WSDL接口webService實(shí)現(xiàn)方式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
簡(jiǎn)單了解Java關(guān)鍵字throw和throws的區(qū)別
這篇文章主要介紹了簡(jiǎn)單了解Java關(guān)鍵字throw和throws的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

