您好,登錄后才能下訂單哦!
這篇文章主要介紹了python線程間通信是如何實(shí)現(xiàn)的,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
線程間通信的幾種實(shí)現(xiàn)方式
首先,要短信線程間通信的模型有兩種:共享內(nèi)存和消息傳遞,以下方式都是基本這兩種模型來實(shí)現(xiàn)的。我們來基本一道面試常見的題目來分析:
題目:有兩個(gè)線程A、B,A線程向一個(gè)集合里面依次添加元素"abc"字符串,一共添加十次,當(dāng)添加到第五次的時(shí)候,希望B線程能夠收到A線程的通知,然后B線程執(zhí)行相關(guān)的業(yè)務(wù)操作。
方式一:使用volatile關(guān)鍵字
基于 volatile 關(guān)鍵字來實(shí)現(xiàn)線程間相互通信是使用共享內(nèi)存的思想,大致意思就是多個(gè)線程同時(shí)監(jiān)聽一個(gè)變量,當(dāng)這個(gè)變量發(fā)生變化的時(shí)候 ,線程能夠感知并執(zhí)行相應(yīng)的業(yè)務(wù)。這也是最簡單的一種實(shí)現(xiàn)方式。
public class TestSync { // 定義一個(gè)共享變量來實(shí)現(xiàn)通信,它需要是volatile修飾,否則線程不能及時(shí)感知 static volatile boolean notice = false; public static void main(String[] args) { List<String> 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收到通知,開始執(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類的wait()和notify()方法
眾所周知,Object類提供了線程間通信的方法: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<String> 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收到通知,開始執(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才開始執(zhí)行,這也正好說明了,notify()方法不釋放鎖,而wait()方法釋放鎖。
方式三:使用JUC工具類 CountDownLatch
jdk1.5之后在java.util.concurrent包下提供了很多并發(fā)編程相關(guān)的工具類,簡化了我們的并發(fā)編程代碼的書寫,***CountDownLatch***基于AQS框架,相當(dāng)于也是維護(hù)了一個(gè)線程間共享變量state。
public class TestSync { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(1); List<String> 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收到通知,開始執(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<String> 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收到通知,開始執(zhí)行自己的業(yè)務(wù)..."); lock.unlock(); }); threadB.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadA.start(); } }
運(yùn)行結(jié)果為:
顯然這種方式使用起來并不是很好,代碼編寫復(fù)雜,而且線程B在被A喚醒之后由于沒有獲取鎖還是不能立即執(zhí)行,也就是說,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<String> list = new ArrayList<>(); // 實(shí)現(xiàn)線程B final Thread threadB = new Thread(() -> { if (list.size() != 5) { LockSupport.park(); } System.out.println("線程B收到通知,開始執(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é)果:
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享python線程間通信是如何實(shí)現(xiàn)的內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。