天天看點

Java典型線程通訊方式等待\通知機制管道流join方法類InheritableThreadLocal的使用

等待\通知機制

  • 如果不使用等待\通知機制,當線程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典型線程通訊方式等待\通知機制管道流join方法類InheritableThreadLocal的使用

要點

在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中的值進行修改,那麼子線程取到的值還是舊值。