等待\通知機制
- 如果不使用等待\通知機制,當線程1要向線程2實作線程通訊,可以通過共享一個變量,線程2可以使用while不停地對某一條件進行檢測,直到線程1對其進行了修改(進而使條件滿足),就實作了一次線程1到線程2的通訊,不過這樣将浪費CPU的資源。
實作
- wait()方法:
- Object類的一個方法,該方法将目前線程置入預執行隊列中,并且在wait()代碼處停止執行,直到接受到通知來或被中斷為止。
- 在調用wait()方法之前,線程必須獲得該對象的對象級别鎖。否則抛出IllegalMonitorStateException,它是RuntimeException的一個子類,不需要捕捉異常。
- 執行wait()方法之後,目前線程釋放鎖。
- wait(long):如果wait方法在long時間内沒有被通知,那麼超過這個時間則自動喚醒。
- notify()/notifyAll()方法:
- Object類的一個方法,該方法用來通知那些可能等待該對象的對象鎖的其他線程,如果有多個線程,則由線程規劃随機挑選出一個呈wait狀态的線程對它發出通知notify,并使它等待擷取該對象的對象鎖。(notifyAll将通知持有該對象的所有其他線程)
- 調用notify()方法前,線程必須獲得該對象的對象級别鎖。否則抛出IllegalMonitorStateException。
- 執行notify()之後,目前線程并不會馬上釋放該對象鎖,呈wait狀态的線程也不能馬上擷取該鎖。在等到執行notify()的方法将程式執行完,也就是退出synchronized代碼塊後,才會開始上述過程。
管道流
示例:
要點
在Java語言中提供了各種各樣的輸入、輸出流,其中管道流(PipeStream)是一種特殊的流,用于在不同線程間直接傳送資料。
Java IO中的管道為運作在同一個JVM中的兩個線程提供了通信的能力。是以管道也可以作為資料源以及目标媒介。
你不能利用管道與不同的JVM中的線程通信(不同的程序)。在概念上,Java的管道不同于Unix/Linux系統中的管道。在Unix/Linux中,運作在不同位址空間的兩個程序可以通過管道通信。在Java中,通信的雙方應該是運作在同一程序中的不同線程。
可以通過Java IO中的
PipedOutputStream
和
PipedInputStream
建立管道。一個PipedInputStream流應該和一個PipedOutputStream流相關聯。一個線程通過PipedOutputStream寫入的資料可以被另一個線程通過相關聯的PipedInputStream讀取出來。
PipedReader
和
PipedWriter
同理。
join方法
示例:
class TestThread implements Runnable{
public void run(){
System.out.println("TestThread");
}
}
public class Test{
public static void main(String[] args){
try{
TestThread testThread = new TestThread();
testThread.start();
testThread.join();
System.out.println("Main");
} catch (InterruptedException e{
e.printStackTrace();
}
}
結果:
TestThread
Main
要點
- 方法join的作用是使被調用的線程對象正常執行run()方法中的任務,而調用線程将進行無限期的阻塞直到被調用線程被銷毀後,才執行後面的代碼。
- 換一種思考方式,方法join具有使線程排隊運作的作用,有些類似于同步的運作效果。
- join()方法執行過程中,如果被執行線程遇到interrupt()方法,那麼将會抛出InterruptedException異常。
- join(long):如果被執行方法在long時間内依舊沒有被銷毀,那麼超過這個時間從join方法中傳回執行後面的代碼。方法join(long)的功能在内部是通過wait(long)實作的,是以也具有釋放鎖的特點。
類InheritableThreadLocal的使用
示例:
public class ThreadLocalParentSon {
//public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();
static class MyThread extends Thread{
@Override
public void run(){
System.out.println("Son Thread = " + threadLocal.get());
}
}
public static void main(String[] args) {
threadLocal.set(new Integer(127));// 目前線程設定值
Thread thread = new MyThread();
thread.start();
System.out.println("Main Tread = " + threadLocal.get());
}
}
Main Tread = 127
Son Thread = 127
若沒有使用 InheritableThreadLocal 之前的輸出結果:
Main Tread = 127
Son Thread = null
要點
- 使用InheritableTreadLocal類可以讓子線程從父線程中取得值。
- 需要注意的一點是,如果子線程在取得值得同時,主線程将InheritableThreadLocal中的值進行修改,那麼子線程取到的值還是舊值。