Java的通過管道來實現執行緒通訊

lightTrace發表於2019-01-17

前言與一點思考

最近使用golang的channel併發程式設計非常爽,又快又方便還不用考慮什麼執行緒不安全的問題,同時在想老相好Java的channel類似實現有沒有呢?
Java四種通訊方式,分別是synchronized關鍵字,while(true)輪詢,wait和notify以及java.io.Pipe,實際上這個java.io.Pipe和go的channel邏輯是相似的。

Java管道的認識

PipedWriter(允許任務向管道寫),和PipedReader(允許不同任務從同一管道中讀取)。管道也可以理解為一個緩衝區,將要讀寫的內容存入到管道,輸入輸出都要從這個管道去操作,管道提供了一個封裝好的解決方案。

程式碼

package cn.com.controller;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author wangjie
 * @version 2018/11/21
 * 任務間使用管道進行輸入輸出
 */
class Sender implements Runnable{
    private Random random = new Random(47);
    private PipedWriter out = new PipedWriter();
    public PipedWriter getPipedWriter(){ return out; }

    @Override
    public void run(){
        try{
                for(int i=0;i<20;i++){
                    out.write(i);
                    //隨機休眠500以內毫秒
                    TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
            }
        }catch(IOException e){
            System.out.println(e + " Sender write exception");
        }catch(InterruptedException e){
            System.out.println(e + " Sender sleep interrupt");
        }
    }
}

class Receiver implements Runnable{
    private PipedReader in;
    public Receiver(Sender sender) throws IOException{
        in = new PipedReader(sender.getPipedWriter());
    }

    @Override
    public void run(){
        try{
                System.out.println("Read: " + (int)in.read() + ",");
        }catch(IOException e){
            System.out.println(e + " Receiver read exception");
        }
    }
}
public class TestPieIo {
    public static void main(String[] args) throws Exception{
        Sender sender = new Sender();
        Receiver receiver = new Receiver(sender);

        ExecutorService exec = Executors.newFixedThreadPool(4);
        exec.execute(sender);
        for (int i=0;i<10;i++) {
          exec.execute(receiver);
        }
        //休眠40秒鐘後中斷
        TimeUnit.SECONDS.sleep(40);
        exec.shutdownNow();
    }
}

輸出:

Read: 0,
Read: 1,
Read: 2,
Read: 3,
Read: 4,
Read: 5,
Read: 6,
Read: 7,
Read: 8,
Read: 9,

並一直阻塞,因為只有十個讀者在讀,只能消費管道的十個資料,所以程式會一直阻塞。

相關文章