Java - 線程間的通信方式

線程通信的方式

線程中通信是指多個線程之間通過某種機制進行協調和交互

線程通信主要可以分為三種方式,分別為共享內存消息傳遞管道流。每種方式有不同的方法來實現

  • 共享內存:線程之間共享程序的公共狀態,線程之間通過讀-寫內存中的公共狀態來隱式通信。
volatile共享內存
  • 消息傳遞:線程之間沒有公共的狀態,線程之間必須通過明確的發送信息來顯示的進行通信。
wait/notify等待通知方式
join方式
  • 管道流
管道輸入/輸出流的形式


共享內存

/*** @Author: Simon Lang* @Date: 2020/5/5 15:13*/
public class TestVolatile {private static volatile boolean flag=true;public static void main(String[] args){new Thread(new Runnable() {public void run() {while (true){if(flag){System.out.println("線程A");flag=false;}}}}).start();
?
?new Thread(new Runnable() {public void run() {while (true){if(!flag){System.out.println("線程B");flag=true;}}}}).start();}
}
?

測試結果:線程A和線程B交替執行


消息傳遞-線程等待和通知

線程等待和通知機制是線程通訊的主要手段之一。

在 Java 中有以下三種實現線程等待的手段 :

Object 類提供的 wait(),notify() 和 notifyAll() 方法;
Condition 類下的 await(),signal() ?和 signalAll() 方法;
LockSupport 類下的 park() 和 unpark() 方法。

Object 類提供的 wait(),notify() 和 notifyAll() 方法;

Object lock = new Object();
new Thread(() -> {synchronized (lock) {try {System.out.println("線程1 -> 進入等待");lock.wait();System.out.println("線程1 -> 繼續執行");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("線程1 -> 執行完成");}
}).start();Thread.sleep(1000);
synchronized (lock) {// 喚醒線程System.out.println("執行 notifyAll()");lock.notifyAll();
}

Condition 類下的 await(),signal() ?和 signalAll() 方法;

// 創建 Condition 對象 (lock 可創建多個 condition 對象)
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 加鎖
lock.lock();
try {// 一個線程中執行 await()condition.await();// 另一個線程中執行 signal()condition.signal();
} catch (InterruptedException e) {e.printStackTrace();
} finally {lock.unlock();
}

Condition 類它可以創建出多個對象。那為什么有了 Object 類的 wait 和 notify 的方式,還需要 condition 來干嘛呢 ?

因為 Object 類的 wait 和 notify 只適用于一個任務隊列,而 Condition 類的 await 和 signal 適用于多個任務隊列,在多個任務隊列的情況下,使用 Object 類的 wait 和 notify 可能會存在線程餓死的問題。

比如以上這種生產者消費者模型,當生產者,消費者(阻塞式的)都有多個的時候,并且此時任務隊列里面沒有任務了,所以消費者就會進入休眠狀態,此時生產者需要做兩件事情 :?

將任務推送到任務隊列
喚醒線程
【問題所在】

① ?此時如果使用 Object 類提供的 wait 和 notify,而喚醒線程是存在兩種可能的:

1)喚醒了消費者?

2)喚醒了生產者

? ? ? ? 如果是喚醒了生產者,那就出問題了,當生產者這邊代碼執行完了就結束了,消費者這邊永遠不會去消費隊列里的任務了,這就會導致線程饑餓問題。
而 Condition 類因為可以被創建多個,所以可以使用兩個 Condition 對象,一個指定喚醒生產者,一個指定喚醒消費者,這樣就不會出現線程饑餓了。

所以 Condition 類的 await 和 signal 是對 Object 類的 wait 和 notify 的一個補充,它解決了 Object 類種分組不明確的問題。

LockSupport 類下的 park() 和 unpark() 方法。

public static void main(String[] args) {Thread t1 = new Thread(() -> {LockSupport.park();System.out.println("線程1被喚醒");},"線程1");t1.start();Thread t2 = new Thread(() -> {LockSupport.park();System.out.println("線程2被喚醒");},"線程2");t2.start();Thread t3 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("喚醒線程2");LockSupport.unpark(t2);},"線程3");t3.start();
}

LockSupport 類又是對 Condition 類的一個補充,它可以指定喚醒某一個線程,它解決了前兩種方式不能隨機指定喚醒線程的問題。

join方式

join()方法的作用是:在當前線程A調用線程B的join()方法后,會讓當前線程A阻塞,直到線程B的邏輯執行完成,A線程才會解除阻塞,然后繼續執行自己的業務邏輯,這樣做可以節省計算機中資源。

public class TestJoin {public static void main(String[] args){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("線程0開始執行了");}});thread.start();for (int i=0;i<10;i++){JoinThread jt=new JoinThread(thread,i);jt.start();thread=jt;}
?}
?static class JoinThread extends Thread{private Thread thread;private int i;
?public JoinThread(Thread thread,int i){this.thread=thread;this.i=i;}
?@Overridepublic void run() {try {thread.join();System.out.println("線程"+(i+1)+"執行了");} catch (InterruptedException e) {e.printStackTrace();}}}
}
?

每個線程的終止的前提是前驅線程的終止,每個線程等待前驅線程終止后,才從join方法返回,實際上,這里涉及了等待/通知機制,即下一個線程的執行需要接受前驅線程結束的通知。


管道輸入/輸出流

管道流是是一種使用比較少的線程間通信方式,管道輸入/輸出流和普通文件輸入/輸出流或者網絡輸出/輸出流不同之處在于,它主要用于線程之間的數據傳輸,傳輸的媒介為管道。

管道輸入/輸出流主要包括4種具體的實現:PipedOutputStrean、PipedInputStrean、PipedReader和PipedWriter,前兩種面向字節,后兩種面向字符。

java的管道的輸入和輸出實際上使用的是一個循環緩沖數組來實現的,默認為1024,輸入流從這個數組中讀取數據,輸出流從這個數組中寫入數據,當這個緩沖數組已滿的時候,輸出流所在的線程就會被阻塞,當向這個緩沖數組為空時,輸入流所在的線程就會被阻塞。

public class TestPip {public static void main(String[] args) throws IOException {PipedWriter writer  = new PipedWriter();PipedReader reader = new PipedReader();//使用connect方法將輸入流和輸出流連接起來writer.connect(reader);Thread printThread = new Thread(new Print(reader) , "PrintThread");//啟動線程printThreadprintThread.start();int receive = 0;try{//讀取輸入的內容while((receive = System.in.read()) != -1){writer.write(receive);}}finally {writer.close();}}
?private static class Print implements Runnable {private PipedReader reader;
?public Print(PipedReader reader) {this.reader = reader;}
?@Overridepublic void run() {int receive = 0;try{while ((receive = reader.read()) != -1){//字符轉換System.out.print((char) receive);}}catch (IOException e) {System.out.print(e);}}}
}
?

?對于Piped類型的流,必須先進性綁定,也就是調用connect()方法,如果沒有將輸入/輸出流綁定起來,對于該流的訪問將拋出異常。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214146.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214146.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214146.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端知識筆記(四十五)———前端開發與后端開發有什么區別

前端開發和后端開發是Web開發中的兩個關鍵領域&#xff0c;它們負責不同的任務和功能。下面是前端開發和后端開發之間的主要區別&#xff1a; 前端開發&#xff1a; 用戶界面&#xff1a;前端開發主要關注用戶界面的開發&#xff0c;包括網頁的布局、樣式、交互等方面。前端技…

Android集成科大訊飛語音識別與語音喚醒簡易封裝

目錄 一、語音喚醒部分 1、首先在科大訊飛官網注冊開發者賬號 2、配置喚醒詞然后下載sdk 3、選擇對應功能下載 4、語音喚醒lib包全部復制到工程目錄下 5、把語音喚醒詞文件復制到工程的assets目錄 6、復制對應權限到AndroidManifest.xml中 7、喚醒工具類封裝 二、語音識…

Linux學習第46天:Linux音頻驅動試驗:能不能?不行也得行。

Linux版本號4.1.15 芯片I.MX6ULL 大叔學Linux 品人間百味 思文短情長 CAN 是目前應用非常廣泛的現場總線之一&#xff0c;主要應用于汽車電子和工業領域&#xff0c;尤其是汽車 領域&#xff0c;汽車上大量的傳感器與模塊都是通過 C…

十二、MapReduce概述

1、MapReduce &#xff08;1&#xff09;采用框架 MapReduce是“分散——>匯總”模式的分布式計算框架&#xff0c;可供開發人員進行相應計算 &#xff08;2&#xff09;編程接口&#xff1a; ~Map ~Reduce 其中&#xff0c;Map功能接口提供了“分散”的功能&#xff…

【Java期末復習資料】(1)知識點總結

本文章主要是知識點&#xff0c;后續會出模擬卷 以下是選擇、填空可能考的知識點&#xff0c;多看幾遍&#xff0c;混個眼熟 面向對象程序設計的基本特征是&#xff1a;抽象、封裝、繼承、多態&#xff08;后三個是三大特性&#xff09;Java源文件的擴綴名是.java編譯Java App…

知識筆記(五十三)———MySQL 刪除數據表

MySQL中刪除數據表是非常容易操作的&#xff0c;但是你在進行刪除表操作時要非常小心&#xff0c;因為執行刪除命令后所有數據都會消失。 語法 以下為刪除 MySQL 數據表的通用語法&#xff1a; DROP TABLE table_name ; -- 直接刪除表&#xff0c;不檢查是否存在 或 DROP…

neuq-acm預備隊訓練week 8 P8794 [藍橋杯 2022 國 A] 環境治理

題目描述 輸入格式 輸出格式 輸出一行包含一個整數表示答案。 輸入輸出樣例 解題思路 最短路二分 AC代碼 #include<bits/stdc.h> using namespace std; long long temp,n, Q; long long f[105][105],min_f[105][105],cut[105],dis[105][105];//cut為減少多少&#x…

寶塔面板部署Apache服務器搭建本地站點發布到公網可訪問【內網穿透】

文章目錄 前言1. 環境安裝2. 安裝cpolar內網穿透3. 內網穿透4. 固定http地址5. 配置二級子域名6. 創建一個測試頁面 正文開始前給大家推薦個網站&#xff0c;前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家…

最短循環節問題 和 最短回文串(kmp , HASH )

給定一個字符串 s&#xff0c;你可以通過在字符串前面添加字符將其轉換為回文串。找到并返回可以用這種方式轉換的最短回文串。 示例 1&#xff1a; 輸入&#xff1a;s "aacecaaa" 輸出&#xff1a;"aaacecaaa"示例 2&#xff1a; 輸入&#xff1a;s &…

Java智慧校園-中小學校園管理系統源碼

智慧校園系統是通過信息化手段&#xff0c;實現對校園內各類資源的有效集成 整合和優化&#xff0c;實現資源的有效配置和充分利用&#xff0c;將校務管理過程的優化協調。為校園提供數字化教學、數字化學習、數字化科研和數字化管理。 致力于為家長和教師提供一個全方位、多層…

scripty妙用

在monorepo項目中&#xff0c;隨著子模塊增多&#xff0c; 每個子項目都需要配置各自的package.json,并且大同小異&#xff0c;為了進一步提高配置效率&#xff0c;引入了scripty&#xff0c;自己寫腳本&#xff0c;直接就可以用哦 1、安裝 npm install scripty --save-dev 2…

ARMday6作業

串口發送指令控制硬件工作 uart1.h #ifndef __UART1_H__ #define __UART1_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_uart.h" void all_led_init(); void led1_on(); void led2_on(); void led3_o…

代碼隨想錄二刷 | 二叉樹 | 110.平衡二叉樹

代碼隨想錄二刷 &#xff5c; 二叉樹 &#xff5c; 110.平衡二叉樹 題目描述解題思路遞歸迭代 代碼實現遞歸法迭代法 題目描述 110.平衡二叉樹 給定一個二叉樹&#xff0c;判斷它是否是高度平衡的二叉樹。 本題中&#xff0c;一棵高度平衡二叉樹定義為&#xff1a; 一個二叉…

EMNLP 2023 獲獎論文公布,大模型、NLP等領域火爆

EMNLP是計算語言學和自然語言處理領域頂級國際會議之一&#xff0c;屬于CCF B類&#xff0c;是由 ACL 下屬的SIGDAT小組主辦的NLP領域頂級國際會議&#xff0c;一年舉辦一次。相較于ACL&#xff0c;EMNLP更偏向于NLP在各個領域解決方案的學術探討。 今年的EMNLP 2023 已于2023…

table表格table/tr/td寬度和高度的設置

關于html中table表格tr,td的?度和寬度 做?頁的時候經常會遇到各種各樣的問題&#xff0c;經常遇到的?個就是會碰到表格被內容撐開的問題。 設置table樣式為 table-layout: fixed; 寬度可以了&#xff0c;但是高度會被撐高。怎么設置都不行&#xff0c;只能給這個td標簽單獨…

【Linux】 線程池

線程池 什么是線程池&#xff1f; 一次預先申請一批線程&#xff0c;讓這批線程有任務&#xff0c;就處理任務&#xff1b;沒任務&#xff0c;就處于等待狀態。 為什么要有線程池&#xff1f; 以空間換時間&#xff0c;預先申請一批線程&#xff0c;當有任務到來&#xff0c;可…

將rtsp視頻流發送到AWS Kinesis Video Streams的方案——使用Gstreamer(C++) Command Line

大綱 1 創建Kinesis Video Streams1.1 創建視頻流1.2 記錄Creation Time 2 創建策略2.1 賦予權限2.2 限制資源2.3 Json格式描述&#xff08;或上面手工設置&#xff09;2.4 注意事項 3 創建IAM用戶3.1 生成密鑰對3.2 附加策略3.3 記錄訪問密鑰對 4 編譯C 創建者庫5 發送6 檢查參…

JavaScript <關于逆向RSA非對稱加密算法的案例(代碼剖析篇)>--案例(五點一)

引用上文: CSDNhttps://mp.csdn.net/mp_blog/creation/editor/134857857 剖析: var bitsPerDigit16; // 每個數組元素可以表示的二進制位數// 數組復制函數&#xff0c;將源數組部分復制到目標數組的指定位置 function arrayCopy(src, srcStart, dest, destStart, n) {var m…

國內地址地區智能解析,無需完整地址也能正確匹配

頁面直接引入使用 已打包成單文件dist/bundle.js 可以直接通過標簽引用 <script src="./bundle.js"></script> <script>var results = AddressParse.parse(福建省福州市福清市石竹街道義明綜合樓3F,15000000000,asseek);console.log(results);…

OD機考真題搜集:服務失效判斷

題目 某系統中有眾多服務,每個服務用字符串(只包含字母和數字,長度<=10)唯一標識,服務間可能有依賴關系,如A依賴B,則當B故障時導致A也故障。 依賴具有傳遞性,如A依賴B,B依賴C,當C故障時導致B故障,也導致A故障。 給出所有依賴關系,以及當前已知故障服務,要求輸…