新聞中心
線程間通信的幾種實(shí)現(xiàn)方式

讓客戶(hù)滿(mǎn)意是我們工作的目標(biāo),不斷超越客戶(hù)的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶(hù),將通過(guò)不懈努力成為客戶(hù)在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:申請(qǐng)域名、網(wǎng)頁(yè)空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、館陶網(wǎng)站維護(hù)、網(wǎng)站推廣。
首先,要短信線程間通信的模型有兩種:共享內(nèi)存和消息傳遞,以下方式都是基本這兩種模型來(lái)實(shí)現(xiàn)的。我們來(lái)基本一道面試常見(jiàn)的題目來(lái)分析:
題目:有兩個(gè)線程A、B,A線程向一個(gè)集合里面依次添加元素"abc"字符串,一共添加十次,當(dāng)添加到第五次的時(shí)候,希望B線程能夠收到A線程的通知,然后B線程執(zhí)行相關(guān)的業(yè)務(wù)操作。
方式一:使用volatile關(guān)鍵字
基于 volatile 關(guān)鍵字來(lái)實(shí)現(xiàn)線程間相互通信是使用共享內(nèi)存的思想,大致意思就是多個(gè)線程同時(shí)監(jiān)聽(tīng)一個(gè)變量,當(dāng)這個(gè)變量發(fā)生變化的時(shí)候 ,線程能夠感知并執(zhí)行相應(yīng)的業(yè)務(wù)。這也是最簡(jiǎn)單的一種實(shí)現(xiàn)方式。
public class TestSync {
// 定義一個(gè)共享變量來(lái)實(shí)現(xiàn)通信,它需要是volatile修飾,否則線程不能及時(shí)感知
static volatile boolean notice = false;
public static void main(String[] args) {
List list = new ArrayList<>();
// 實(shí)現(xiàn)線程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("線程A向list中添加一個(gè)元素,此時(shí)list中的元素個(gè)數(shù)為:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
notice = true;
}
});
// 實(shí)現(xiàn)線程B
Thread threadB = new Thread(() -> {
while (true) {
if (notice) {
System.out.println("線程B收到通知,開(kāi)始執(zhí)行自己的業(yè)務(wù)...");
break;
}
}
});
// 需要先啟動(dòng)線程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再啟動(dòng)線程A
threadA.start();
}
} 運(yùn)行結(jié)果為:
方式二:使用Object類(lèi)的wait()和notify()方法
眾所周知,Object類(lèi)提供了線程間通信的方法:wait()、notify()、notifyaAl(),它們是多線程通信的基礎(chǔ),而這種實(shí)現(xiàn)方式的思想自然是線程間通信。
注意: wait和 notify必須配合synchronized使用,wait方法釋放鎖,notify方法不釋放鎖。
public class TestSync {
public static void main(String[] args) {
// 定義一個(gè)鎖對(duì)象
Object lock = new Object();
List list = new ArrayList<>();
// 實(shí)現(xiàn)線程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("線程A向list中添加一個(gè)元素,此時(shí)list中的元素個(gè)數(shù)為:"+
list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
lock.notify();// 喚醒B線程
}
}
});
// 實(shí)現(xiàn)線程B
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
if (list.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("線程B收到通知,開(kāi)始執(zhí)行自己的業(yè)務(wù)...");
}
}
});
// 需要先啟動(dòng)線程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再啟動(dòng)線程A
threadA.start();
}
} 運(yùn)行結(jié)果為:
由打印結(jié)果截圖可知,在線程A發(fā)出notify()喚醒通知之后,依然是走完了自己線程的業(yè)務(wù)之后,線程B才開(kāi)始執(zhí)行,這也正好說(shuō)明了,notify()方法不釋放鎖,而wait()方法釋放鎖。
方式三:使用JUC工具類(lèi) CountDownLatch
jdk1.5之后在java.util.concurrent包下提供了很多并發(fā)編程相關(guān)的工具類(lèi),簡(jiǎn)化了我們的并發(fā)編程代碼的書(shū)寫(xiě),***CountDownLatch***基于AQS框架,相當(dāng)于也是維護(hù)了一個(gè)線程間共享變量state。
public class TestSync {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
List list = new ArrayList<>();
// 實(shí)現(xiàn)線程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("線程A向list中添加一個(gè)元素,此時(shí)list中的元素個(gè)數(shù)為:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
countDownLatch.countDown();
}
});
// 實(shí)現(xiàn)線程B
Thread threadB = new Thread(() -> {
while (true) {
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("線程B收到通知,開(kāi)始執(zhí)行自己的業(yè)務(wù)...");
break;
}
});
// 需要先啟動(dòng)線程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再啟動(dòng)線程A
threadA.start();
}
} 運(yùn)行結(jié)果為:
方法四:使用ReentrantLock結(jié)合Condition
public class TestSync {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
List list = new ArrayList<>();
// 實(shí)現(xiàn)線程A
Thread threadA = new Thread(() -> {
lock.lock();
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("線程A向list中添加一個(gè)元素,此時(shí)list中的元素個(gè)數(shù)為:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
condition.signal();
}
lock.unlock();
});
// 實(shí)現(xiàn)線程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 5) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("線程B收到通知,開(kāi)始執(zhí)行自己的業(yè)務(wù)...");
lock.unlock();
});
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
} 運(yùn)行結(jié)果為:
顯然這種方式使用起來(lái)并不是很好,代碼編寫(xiě)復(fù)雜,而且線程B在被A喚醒之后由于沒(méi)有獲取鎖還是不能立即執(zhí)行,也就是說(shuō),A在喚醒操作之后,并不釋放鎖。這種方法跟 Object 的 wait() 和 notify() 一樣。
方式五:基于LockSupport實(shí)現(xiàn)線程間的阻塞和喚醒
LockSupport 是一種非常靈活的實(shí)現(xiàn)線程間阻塞和喚醒的工具,使用它不用關(guān)注是等待線程先進(jìn)行還是喚醒線程先運(yùn)行,但是得知道線程的名字。
public class TestSync {
public static void main(String[] args) {
List list = new ArrayList<>();
// 實(shí)現(xiàn)線程B
final Thread threadB = new Thread(() -> {
if (list.size() != 5) {
LockSupport.park();
}
System.out.println("線程B收到通知,開(kāi)始執(zhí)行自己的業(yè)務(wù)...");
});
// 實(shí)現(xiàn)線程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("線程A向list中添加一個(gè)元素,此時(shí)list中的元素個(gè)數(shù)為:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
LockSupport.unpark(threadB);
}
});
threadA.start();
threadB.start();
}
} 運(yùn)行結(jié)果:
python學(xué)習(xí)網(wǎng),大量的免費(fèi)python視頻教程,歡迎在線學(xué)習(xí)!
本文題目:創(chuàng)新互聯(lián)Python教程:python 線程間通信用什么手段
URL分享:http://www.dlmjj.cn/article/cdppghe.html


咨詢(xún)
建站咨詢(xún)
