1. 程式人生 > >(Java多執行緒系列七)Java記憶體模型和執行緒的三大特性

(Java多執行緒系列七)Java記憶體模型和執行緒的三大特性

Java記憶體模型和執行緒的三大特性

多執行緒有三大特性:原子性、可見性、有序性

1、Java記憶體模型

Java記憶體模型(Java Memory Model ,JMM),決定一個執行緒對共享變數的寫入時,能對另一個執行緒可見。從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(main memory)中,每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。

用一張圖表示Java記憶體模型

2、原子性

原子性即一個操作或多個操作,要麼全部執行並且執行過程不被任何因素打斷,要麼就都不執行。

一個經典的例子就是資料庫儲存的事務。原子性其實就是保證資料一致、執行緒安全的一部分。

Synchronized、lock可以解決執行緒原子性問題

3、可見性

當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。

若兩個執行緒在不同的cpu,那麼執行緒1改變了i的值還沒重新整理到主存,執行緒2又使用了i,那麼這個i值肯定還是之前的,執行緒1對變數的修改其他執行緒沒看到,這就是可見性問題。

Volatile可以解決執行緒可見性問題

4、有序性

程式執行的順序按照程式碼的先後順序執行。

一般來說處理器為了提高程式執行效率,可能會對輸入程式碼進行優化,它不保證程式中各個語句的執行先後順序同程式碼中的順序一致,但是它會保證程式最終執行結果和程式碼順序執行的結果是一致的。

而在多執行緒就不一定了,所以我們在多執行緒程式設計時就得考慮這個問題了。

5、volatile關鍵字可以解決執行緒之間可見性的問題

class ThreadDemo extends Thread {

    boolean flag;

    ThreadDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println(getName() + "執行緒開始執行。。。");
        while (flag) {
        }
        System.out.println(getName() + "執行緒已經結束。。。");
    }

    public void stopThread() {
        this.flag = false;
    }
}

public class VolatileThreadDemo {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo(true);
        threadDemo.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadDemo.stopThread();
    }
}

先來看以上的程式碼,在主執行緒中呼叫threadDemo.stopThread()方法停止執行緒,看上去邏輯沒有問題,但是我們會發現執行緒沒有停止。

注意:有的同學可能在測試上面程式碼的時候程式可以正常退出。那是因為你的JVM沒有優化造成的!

造成執行緒沒有停止的原因是while(flag)中的flag是線上程執行的“工作記憶體”中獲取的,而不是從“主記憶體”中獲取的,這就造成了我們在主執行緒中改變flag的值對於子執行緒中不生效。只要在flag前加volatile關鍵字,強制執行緒每次讀取該值的時候都去“主記憶體”中取值,就能解決我們的問題。

package com.littlestones.volatiledemo;

/**
 * @program: JavaThreadLearn
 * @description: volatile示例
 * @author: Leil
 * @create: 2019-12-24 15:22
 */

class ThreadDemo extends Thread {

    volatile boolean flag;

    ThreadDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println(getName() + "執行緒開始執行。。。");
        while (flag) {
        }
        System.out.println(getName() + "執行緒已經結束。。。");
    }

    public void stopThread() {
        this.flag = false;
    }
}

public class VolatileThreadDemo {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo(true);
        threadDemo.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadDemo.stopThread();
    }
}

注意:volatile關鍵字只能解決執行緒的可見性問題,不能解決執行緒的原子性問題