【多線程】線程間通信

等待通知機智的實現

方法wait()的作用是使當前執行代碼的線程進行等待,wait()方法是Object類的方法,該方法用來將當前線程置入“預執行隊列”中,並且在wait()所在的代碼行處停止執行,直到接到通知或被中斷爲止。

在調用wait()之前,線程必須獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。

在執行wait()方法後,當前線程釋放鎖。

如果調用wait()是沒有持有適當的鎖,則拋出IIegalMonitorStateException,它是RuntimeException的一個子類。

方法notify()也要在同步方法或同步代碼塊中調用,即在調用前,線程也必須獲得該對象的對象級別鎖。

該方法用來通知那些可能等待該對象的對象鎖的其他線程,如果有多個線程等待,則由線程規劃器隨機挑選出其中一個呈wait狀態的線程,對其發出通知notify,並使它等待獲取該對象的對象鎖。

 

需要說明的是,在執行notify()方法後,當前線程不會馬上釋放該對象鎖,呈wait狀態的線程也並不能馬上獲取該對象鎖,要等到執行notify()方法的線程將程序執行完,也就是退出synchronized代碼塊後,當前線程纔會釋放鎖,而呈wait狀態的線程纔可以獲取該對象的鎖。

當第一個獲得了該對象鎖的wait線程運行完畢後,他會釋放掉該對象鎖,此時如果該對象沒有再次使用notify語句,則即便該對象已經空閒,其他wait狀態等待的線程由於沒有得到該對象的通知,還會繼續阻塞在wait狀態,直到這對象發出一個notify或notifyall.

用一句話來總結一下wait和notify:

wait使線程停止運行,而notify使停止的線程繼續運行。

 

以上說法創建測試項目

wait_notify_size5

創建MyList類

public class MyList {

    private static List list = new ArrayList();

    public static void add() {
        list.add("anything");
    }

    public static int size() {
        return list.size();
    }
}

類ThreadA

public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock){
        super();
        this.lock = lock;
    }

    @Override
    public void run() {

        try {
            synchronized (lock){
                if(MyList.size() != 5){
                    System.out.println("wait begin"+System.currentTimeMillis());
                    lock.wait();
                    System.out.println("wait end"+ System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

類ThreadB

public class ThreadB extends Thread {
    private Object lock;

    public ThreadB(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {

        try {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add();
                    if (MyList.size() == 5) {
                        lock.notify();
                        System.out.println("已發出通知");

                    }
                    System.out.println("添加了" + (i + 1) + "個元素!");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

類Run

public class Run {

    public static void main(String[] args) {

        try {
            Object lock = new Object();
            ThreadA a = new ThreadA(lock);
            a.start();
            Thread.sleep(50);
            ThreadB b = new ThreadB(lock);
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果如下

日誌信息 wait end 在最後輸出,這也說明notify()方法執行後並不是立即釋放鎖。

關鍵字synchronized可以將任何一個Object對象作爲同步對象來看待,而java爲每個對象都實現了wait()和notify()方法,它們必須用在被synchronized同步的Object的臨界區內。通過調用wait()方法可以使處於臨界區內的線程進入等待狀態,同時釋放被同步對象的鎖。而notify操作可以喚醒一個因調用了wait操作而處於阻塞狀態的線程,使其進入就緒狀態。被重新喚醒的線程會試圖重新獲得臨界區的控制權,也就是鎖,並繼續執行臨界區內wait之後的代碼。

如果發出的notify操作時沒有處於阻塞狀態中的線程,那麼該命令就會被忽略。

 

wait()方法可以使用調用該方法的線程釋放共享資源的鎖,然後從運行狀態退出,進入等待隊列,直到被再次喚醒。

 

notify()方法可以隨機喚醒等待隊列中等待同一共享資源的“一個”線程,並是該線程退出等待隊列,進入可運行狀態,也就是notify()方法僅通知“一個”線程。

 

notifyAll()方法可以使所有正在等待隊列中等待同一共享資源的“全部”線程從等待狀態退出,進入可運行狀態。此時,優先級最高的那個線程最先執行,但也有可能是隨機執行,因爲這要取決於JVM虛擬機的實現。

 

方法notify()被執行後,不釋放鎖,必須執行完notify()方法所在的同步代碼塊synchronized代碼塊後才釋放鎖。

當線程呈wait()狀態時,調用線程對象的interrupt()方法會出現InterruptedException異常。

 

方法notify()僅隨機喚醒一個線程。

當多次調用notify()方法時,會隨機將等待wait狀態的線程進行喚醒。

 

notifyAll()喚醒所有的線程

wait(long)

方法wait(long)的功能是等待某一段時間內是否有線程對鎖進行喚醒,如果超過這個時間則自動喚醒。

通過管道進行線程間通信:字節流

 

管道流是一種特殊的流,用於在不同線程間直接傳送數據。一個線程發送數據到輸出管道,另一個線程從輸入管道中讀數據。通過使用管道,實現不同線程間的通信,而無需藉助於累死臨時文件之類的東西。

在Java的jdk中提供了4個類來使線程間可以進行通信:

1)PipedInputStream和PipedOutputStream

2)PipedReader和PipedWriter

創建測試項目pipeInputOutPut

類ReadData
public class ReadData {
    public void readMethod(PipedInputStream input){

        try {
            System.out.println("read :");
            byte[] byteArray = new byte[20];

            int readLength = input.read(byteArray);

            while (readLength != -1){
                String newData = new String(byteArray, 0, readLength);
                System.out.println("讀取到:"+newData);
                readLength = input.read(byteArray);
            }
            System.out.println();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

類WriteData

public class WriteData {
    public void writeMethod(PipedOutputStream out){
        try {
            System.out.println("write :");
            for (int i = 0; i < 300; i++) {
                String outData = ""+(i+1);
                out.write(outData.getBytes());
                System.out.println("寫入:"+outData);
            }
            System.out.println();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

類ThreadRead

public class ThreadRead extends Thread {

    private ReadData read;
    private PipedInputStream input;

    public ThreadRead(ReadData read, PipedInputStream input) {

        super();
        this.read = read;
        this.input = input;
    }

    @Override
    public void run() {
        read.readMethod(input);
    }
}

類ThreadWrite

public class ThreadWrite extends Thread {

    private WriteData write;
    private PipedOutputStream out;

    public ThreadWrite(WriteData write, PipedOutputStream out){
        super();
        this.write = write;
        this.out = out;
    }

    @Override
    public void run() {
        write.writeMethod(out);
    }
}

類Run

public class Run {

    public static void main(String[] args) {

        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            PipedInputStream inputStream = new PipedInputStream();
            PipedOutputStream outputStream = new PipedOutputStream();

            //使兩個Stream之間產生通信鏈接,這樣纔可以將數據進行輸出與輸入
            outputStream.connect(inputStream);
            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            threadRead.start();
            Thread.sleep(2000);
            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            threadWrite.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果

 

通過管道進行線程間通信:字符流

創建測試項目

pipeReaderWriter
類ReadData
public class ReadData {

    public void readMethod(PipedReader input) {
        try {
            System.out.println("read: ");
            char[] byteArray = new char[20];
            int readLength = input.read(byteArray);
            while (readLength != -1) {
                String newData = new String(byteArray, 0, readLength);
                System.out.println("讀數據:"+newData);
                readLength = input.read(byteArray);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

類WriteData

public class WriteData {

    public void writeMethod(PipedWriter out) {
        try {
            System.out.println("write: ");
            for (int i = 0; i < 300; i++) {
                String outData = "" + (i + 1);
                out.write(outData);
                System.out.println("寫入數據:"+outData);
            }
            System.out.println();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

類ThreadRead

public class ThreadRead extends Thread {


    private ReadData read;
    private PipedReader input;

    public ThreadRead(ReadData read, PipedReader input) {
        super();
        this.read = read;
        this.input = input;
    }

    @Override
    public void run() {
        read.readMethod(input);
    }
}

類ThreadWrite

public class ThreadWrite extends Thread {

    private WriteData write;
    private PipedWriter out;

    public ThreadWrite(WriteData write, PipedWriter out){
        super();
        this.write = write;
        this.out = out;
    }

    @Override
    public void run() {
     write.writeMethod(out);
    }
}

類Run

public class Run {
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            PipedReader inputStream = new PipedReader();
            PipedWriter outputStream = new PipedWriter();
            outputStream.connect(inputStream);
            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            threadRead.start();
            Thread.sleep(2000);
            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            threadWrite.start();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

運行結果

join()方法的使用

作用說明:

如果子線程中要進行大量的耗時運算,主線程往往早於子線程結束之前結束。這時,如果主線程想等待子線程執行完成之後再結束,比如子線程處理一個數據,主線程要取得這個數據中的值,就要用到join()方法了。

join()的作用就是等待線程對象銷燬。

創建測試項目

joinTest

 

創建類

MyThread
public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            int secondValue = (int) (Math.random() * 10000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

創建類

Test
public class Test {
    public static void main(String[] args) {

        try {
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join();
            System.out.println("我想當ThreadTest對象執行完畢後我再執行,我做到了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果

結論:

方法join具有使線程排隊運行的作用,有些類似同步的運行效果。join與synchronized的區別是:join在內部使用wait()方法進行等待,而sychronized關鍵字使用的是“對象監視器”原理作爲同步。

 

join(long)參數是設定等待時間。

join(long)與sleep(long)的區別

  • 方法join(long)的功能在內部是使用wait(long)方法來實現的,所以join(long)方法具有釋放鎖的特點。
  • sleep(long)方法不釋放鎖

類ThreadLocal的使用

類ThreadLoacal主要解決的就是每個線程都綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有數據。

類ThreadLocal解決的是變量在不同線程間的隔離性,也就是不同線程擁有自己的值,不同線程中的值可以放入ThreadLocal類中進行保存的。

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章