下面小編就為大家帶來一篇java多執行緒程式設計學習(線程間通訊)。小編覺得蠻不錯的,現在就分享給大家,也給大家做個參考。一起跟著小編過來看看吧
一、概要
#線程是作業系統中獨立的個體,但這些個體若不經過特殊的處理就不能成為一個整體,線程間的通訊就是成為整體的必用方案之一。可以說,使執行緒進行通訊後,系統之間的互動性會更強大,在大幅提高cpu利用率的同時也會使程式設計師對各執行緒任務在處理過程中進行有效的把控和監督。
二、等待/通知機制
1、"wait/notify"機制:等待/通知機制, wait使執行緒暫停運行,而notify 使暫停的執行緒繼續運行。用一個廚師和服務生的互動來說明:
(1) 服務生取到菜的時間取決於廚師,所以服務生就有「等待」(wait)的狀態。
(2) 廚師將菜放在「菜品傳遞台」上,其實就相當於一種通知(notify),這時服務員才可以拿到菜並交給就餐者。
2、wait()
(1) 使目前執行程式碼的執行緒進行等待。 wait()方法是Object類別的方法,該方法用來將目前執行緒置入「預執行佇列」中,並且在wait()所在的程式碼行處停止執行,直到接收通知或中斷為止。
(2) 在呼叫wait()方法之前,執行緒必須取得該物件的物件級鎖,即只能在同步方法或同步區塊中呼叫wait()方法,否則拋出IllegalMonitorStateException例外。 (屬於Runtime的子類,不需要try-catch 語句進行捕捉異常)
(3) 在呼叫wait()方法之後,當前執行緒釋放鎖,而此物件會進入執行緒等待池中,等待被喚醒。在從wait()返回前,線程與其他呈wait線程競爭重新獲得鎖。
(4) wait()方法可以被interrupt 打斷並拋出InterruptedException。
(5) wait(long):帶一個參數的wait(long)方法的功能是等待某一時間內是否有執行緒對鎖進行喚醒,如果超過這個時間則自動喚醒。
3、notify()
(1) 用來通知那些可能等待該物件的物件鎖的其他執行緒。如果有多個線程等待,則由線程規劃器隨機挑選出其中一個呈wait狀態的線程,對其發出通知notify,並使它等待獲取該物件的物件鎖。 (注意!這裡說的是等待,即在執行完notify()方法後,當前線程不會馬上釋放該物件鎖,即wait()狀態的線程也不會馬上獲得物件鎖,需要將synchronized 程式碼區塊中的程式碼執行完後才釋放鎖定!鎖,否則也會拋出IllegalMonitorStateException.
(3)當notify()發出通知,卻沒有wait()執行緒在等待時,則不作作用。
4、notifyAll()(1) 可以使所有正在等待隊列中的等待同一共享資源(即同一個鎖) 的"全部"線程從等待狀態退出,進入可運作狀態。
5、#6、
假死:「假死「現像其實就是線程進入WAITING等待狀態。如果全部執行緒都進入WAITING狀態,程式就不再執行任何功能了,整個專案呈現停止狀態。 出現這樣的原因是因為:例如多個生產者和多個消費者的問題,“生產者”可能喚醒“生產者”,“消費者”可能喚醒“消費者”,喚醒了同類,導致線程不斷在等待。怎麼解決這個問題呢?將notify() 改成 notifyAll()方法即可,也就是將異類一同喚醒就可以了。
7、Jave中 管道流(pipeStream)是一種特殊的流,可用來在不同的執行緒中直接傳送資料。一個執行緒發送資料到輸出管道,另一個執行緒從輸入管道中讀取資料。透過使用管道,實現不同線程間的通信,而無須借助於類似臨時文件之類的東西。 JDK中提供了四個類別來使線程間可以通信,其中包括位元組流(PipedOutputStream、PipedInputStream)和字元流(PipedWriter、PipedReader)。
#
public class Run { public static void main(String[] args) { try { WriteData writeData = new WriteData(); ReadData readData = new ReadData(); PipedOutputStream outputStream = new PipedOutputStream(); PipedInputStream inputStream = new PipedInputStream(); outputStream.connect(inputStream);//使两个Stream之间产生通信链接,这样才可以将数据进行输入输出 ThreadRead threadRead = new ThreadRead(readData, inputStream); threadRead.start(); Thread.sleep(1000); ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream); threadWrite.start(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
三、方法join的使用 1、在很多情況下,主線程創建並啟動子線程,如果子線程中要進行大量的耗時計算,主線程往往將早於子線程結束之前結束。這時,如果主執行緒想等待子執行緒執行完成之後再結束,例如子執行緒處理一個數據,主執行緒要取得這個數據中的值,就要用到join()方法了。 2、join()的作用是等待執行緒銷毀,而使 當前執行緒進行無限期的阻塞,等待join()的執行緒銷毀後再繼續執行目前執行緒的程式碼。 3、同樣的,join()方法可以被interrupt()方法打斷並拋出InterruptedException異常。 4、join 與 synchronized 的差別? (1) join()在內部使用wait() 方法進行等待。 (2) synchronized 關鍵字使用的是「物件監視器」原理作為同步。 5、方法join(long) 和 sleep(long)的差別? (1) join(long)內部採用wait(long)方法實現,當執行wait(long)方法後,當前執行緒的鎖被釋放,那麼其他執行緒也可以呼叫此執行緒中的同步方法了。即 join(long)之後,該執行緒釋放鎖,又需要和其他執行緒去爭搶鎖的資源。 (2) Thread.sleep(long)方法不釋放鎖定。 四、類別ThreadLocal 的使用 #1、變數值的共用可以使用public static 變數的形式,所有的線程都使用同一個public static 變數。如果想實現每一個執行緒都有自己的共享變數該如何解決呢?類別ThreadLocal解決的是每個執行緒綁定自己的值,可以將ThreadLocal類別比喻為全域存放資料的盒子,盒子中可以存放每個執行緒的私有資料。 2、類別ThreadLocal 具有隔離性,也就是每個執行緒都可以存入自己執行緒的資料而互不影響,而取到的也是自己執行緒存入的資料。
#五、類別InheritableThreadLocal的使用 ##1、InheritableThreadLocal類別繼承於ThreadLocal類,所以它具有ThreadLocal類別的特性,但又是一個特殊的ThreadLocal,其特殊性在於InheritableThreadLocal變數值會自動傳遞給所有子執行緒,而普通ThreadLocal變數不行;而且,透過重寫這個類別中的 childValue 方法,子執行緒的值可以當父線程值的一個任意函數。 備註: (1) 什麼是子執行緒? 包含在 Thread thread = new Thread(new ThreadStart(delegate{ (2) 什麼是主執行緒? UI介面和Main函數皆為主線程,除了「不包含在Thread裡面的程式」皆可 視為主執行緒。 (個人理解)
}));裡面皆視為子執行緒。 (個人理解)
以上是Java多執行緒之執行緒間通訊詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!