(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關鍵字只能解決線程的可見性問題,不能解決線程的原子性問題

源碼地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM