JMM——線程間通信

在併發編程中,我們需要處理兩個關鍵問題:

1. 線程之間如何通信

  通信是指線程之間以何種機制來交換信息,在命令式編程(c語言)中,線程之間的通信機制有兩種:

  1.1 共享內存

  在共享內存的併發模型裏,線程之間共享程序的公共狀態,線程之間通過寫-讀內存中的公共狀態來隱式進行通信。

 JAVA併發採用的是共享內存模型java線程之間的通信總是隱式進行,整個通信過程對程序員完全透明。

  1.2 消息傳遞

  在消息傳遞的併發模型裏,線程之間沒有公共狀態,線程之間必須通過明確的發送消息來進行通信 

2. 線程之間如何同步

  同步是指程序用於控制不同線程之間操作發生相對順序的機制。

  2.1 在共享內存併發模型裏,同步是顯示進行的

  2.2 在消息傳遞的併發模型裏,由於消息的發送必須在消息的接收之前,因此同步是隱式的

 


共享變量 = 實例域(對象)、靜態域和數組元素

共享變量存儲在堆內存中,而堆內存在線程之間共享


局部變量,方法定義參數,異常處理參數不會在線程之間共享

它們不會有內存可見性問題,也不受內存模型的影響


JMM(JAVA MEMORY MODEL)

java線程之間的通信由JMM控制

 

JMM決定一個線程對共享變量的寫入何時對另一個線程可見


MM定義了線程和主內存之間的抽象關係:

線程之間的共享變量存儲在主內存中

每個線程都有一個私有的本地內存,本地內存中存儲了該線程以讀/寫共享變量的副本,本地內存是JMM的一個抽象概念,並不真實存在。

它涵蓋了緩存,寫緩衝區,寄存器以及其他的硬件和編譯器優化。


JMM的抽象示意圖:


從上圖來看,線程A和線程B之間要通信的話,需要經歷下面2個步驟:

1. 線程A把本地內存中更新過的共享變量刷新到主內存中去

2. 線程B到主內存中去讀取線程A之前已更新過的共享變量


JMM通過控制主內存與每個線程的本地內存之間的交互來爲java程序員提供內存可見性保證


JAVA還有種方式可以用於線程間通信——管道流

操作和普通IO並無二致,只是需要把管道連接起來

PipedOutputStream內部使用PipedInputStream接收需要輸出的字節


用管道流實現生產者和消費者模式:

生產者:

public class Producer implements Runnable {

    private PipedOutputStream pos;

    public Producer(PipedOutputStream pos) {
        this.pos = pos;
    }

    @Override
    public void run() {
        try {
            pos.write("Hello World".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                pos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

消費者:

public class Consumer implements Runnable {

    private PipedInputStream pis;

    public Consumer(PipedInputStream pis) {
        this.pis = pis;
    }

    @Override
    public void run() {
        // 將數據保存在byte數組中
        byte[] bytes = new byte[100];
        try {
            // 從數組中得到實際大小。
            int length = pis.read(bytes);
            System.out.println(new String(bytes, 0, length));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                pis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

測試類:

public class TestPipedStream {
    public static void main(String[] args) {
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pis = new PipedInputStream();
        try {
            // 連接管道
            pos.connect(pis);
            new Thread(new Producer(pos)).start();
            new Thread(new Consumer(pis)).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

運行可以看到Hello World






發佈了39 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章