Java多線程高併發面試(一)-volatile

目錄

1.volatile

1.1 volatile是什麼?

1.2 JMM內存模型之可見性

1.3 可見性的代碼驗證

1.4 volatile不保證原子性

1.5 volatile不保證原子性理論解釋

1.6 volatile不保證原子性問題解決

1.7 volatile指令重排序案例1

1.8 volatile指令重排序案例2

1.9 單利模式在多線環境下存在的安全性問題

1.10 單例模式volatile分析


1.volatile

1.1 volatile是什麼?

1.概念: volatile是JVM提供的一種輕量級同步機制

2.特性

(1)保證可見性Q1:什麼是可見性?

(2)不保證原子性Q2:什麼是原子性

(3)禁止指令重排序Q3:什麼指令重排序?

 想要理解volatile的特性,需要解決上面的靈魂三問,請繼續往下看。

1.2 JMM內存模型之可見性

1.概念:JMM是全稱是Java Memory Model ,即Java內存模型,它是一組規則或規範,用於定義程序中各個變量的訪問方式;是抽象的概念,並不真實存在。簡而言之:JMM是一種約束,用於定義內存中各個變量的讀寫方式。

2.JMM的同步規定

(1)線程加鎖前,必須從主存中讀取共享變量的最新值到線程自己的工作內存中。Q1:什麼是線程自己的工作內存?

(2)線程解鎖前,必須將線程自己工作內存中的值刷回主存中。

(3)加鎖解鎖必須是同一把鎖。

3.線程的不可見性

(1) 概念:線程自己的工作內存:Java程序運行的實體是線程,JVM爲每個線程都分配一個私有的內存空間(私有內存空間歸該線程獨享,其他線程不能訪問),這個私有內存空間稱之爲線程自己的工作內存(2.Q1)。

(2)線程訪問共享變量的方式:

場景:某一時刻,線程1,線程2同時訪問主存中的變量num,線程1將num的修改爲num=8。

過程

         ①線程1,線程2先將主存中共享num的值拷貝一個副本到自己線程的工作內存中。此時,線程1,線程2中的副本 num = 5。

         ②線程1修改自己工作內存中副本 num = 8。

         ③線程1的副本num的值修改後,會立即刷回主存,此時主存的 num = 8。

         ④主存中num的值變化後,並沒有通知線程2重新從主存中拷貝num最新的值,此時,線程2中的副本 num = 5。

結論:線程1對共享變量num的修改對線程2是不可見的現象,稱之爲線程不可見性

概念線程對共享變量的修改,不能通知其他線程看見稱之爲線程的不可見性反之線程對共享變量的修改,能夠立即通知其他線程看見,稱之爲線程的可見性。

4.JMM三大特性

(1)可見性

(2)原子性

(3)有序性

1.3 可見性的代碼驗證

(1)不可見性驗證

/**
 * 驗證volatile的可見性
 */
public class VisibilityDemo {

    //共享變量 5
    public static /*volatile*/ int num = 5;

    public static void main(String[] args) {
        //開啓線程thread-1
        new Thread(() -> {
            //休眠2秒
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改num的值
            num = 8;
            System.out.println("thread-1 修改了num的值:" + num);
        }, "thread-1").start();

        //開啓線程thread-1
        new Thread(() -> {
            System.out.println("thread-2獲取num="+num);
            while (true) {
                if (num != 5) {
                    System.out.println("thread-2 獲取了修改後的num="+num);
                    break;
                }
            }
        }, "thread-2").start();
    }
}

運行結果:

(2)可見性驗證

   用volatile修飾變量 num : public static volatile int num = 5;

/**
 * 驗證volatile的可見性
 */
public class VisibilityDemo {

    //共享變量 5
    public static volatile int num = 5;

    public static void main(String[] args) {
        //開啓線程thread-1
        new Thread(() -> {
            //休眠2秒
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改num的值
            num = 8;
            System.out.println("thread-1 修改了num的值:" + num);
        }, "thread-1").start();

        //開啓線程thread-1
        new Thread(() -> {
            System.out.println("thread-2獲取num="+num);
            while (true) {
                if (num != 5) {
                    System.out.println("thread-2 獲取了修改後的num="+num);
                    break;
                }
            }
        }, "thread-2").start();
    }
}

運行結果: 

1.4 volatile不保證原子性

(1)什麼是原子性?

原子性是指不可以分割的整體,線程處理具體業務時,處理過程中不能被加塞或者打斷,需要整體完整,業務要麼同時成功,要麼同時失敗。

(2)volatile不保證原子性代碼驗證

/**
 * volatile不能保證原子性
 */
public class AtomDemo {
    //字段
    private int sum = 0;

    /**
     * 自增
     */
    public void add(){
        sum ++;
    }

    public static void main(String[] args) {
        //共享變量
        AtomDemo atomDemo = new AtomDemo();

        //開啓20個線程
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    atomDemo.add();
                }
            },String.valueOf(i)).start();
        }

        //等待上面20個線程執行完畢,main輸出sum的值
        while(Thread.activeCount()>2){ //最小兩個線程,main,gc
            //如果線程數大於2,main線程讓出執行權
            Thread.yield();
        }

        //main線程輸出sum的值
        System.out.println("20個線程,每個線程加1000次,理論結果=20000?而實際結果="+atomDemo.sum);
    }

 運行結果:

1.5 volatile不保證原子性理論解釋

1.6 volatile不保證原子性問題解決

1.7 volatile指令重排序案例1

1.8 volatile指令重排序案例2

1.9 單利模式在多線環境下存在的安全性問題

1.10 單例模式volatile分析

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