Volatile不能保證線程安全

volatile是什麼?

標記變量是“易變的”,要求線程使用變量時,必須直接從主存獲取;線程修改完畢後,必須保證一氣呵成的寫回主存。

volatile爲什麼?

需要先了解java內存模型。
Java內存模型在主內存與線程之間,爲了提高速度,又多了工作內存。工作內存與主內存的關係,類似電腦內存與硬盤的關係,緩存數據,提高速度,但可能存在不一致問題。
volatile希望在一定程度上保證一致性。
但在併發的3大特徵裏,volatile實現了可見性。即如果修改,修改結果對其他線程可見。

volatile如何實現的?

待補充。

volatile的侷限

雖然volatile保證了從主內存讀用和回寫到主內存,但併發情況下,還是可能發生2個線程先後腳的讀寫共享變量,所以不能保證線程安全。
jvm取值、++操作碼getstatic指令把increase的值拿到了操作棧的頂部,此時由於volatile的規則,該值是正確的。
iconst_1和iadd指令在執行的時候increase的值很有可能已經被其他線程加大,此時棧頂的值過期。
putstatic指令接着把過期的值同步回主存,導致了最終結果較小。

實驗

package com.master;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by 10171512 on 2020/4/17.
 * 1)驗證volatile不能保證線程安全
 *  1.共享變量:類靜態
 *  2.多線程讀寫
 * 2)對照組:普通變量和原子變量
 */
public class D13VolatileTest extends Thread{
    private static volatile int volInt=0;
    private static int normalInt=0;
    private static AtomicInteger atomInt=new AtomicInteger();
    private void increment(){
        volInt++;
        normalInt++;
        atomInt.incrementAndGet();
    }

    @Override
    public void run() {
        for (int i = 0; i <10000 ; i++) {
            increment();

        }
    }
    public static void get() {
        System.out.println("volInt:"+volInt+"; normalInt:"+normalInt+"; atomInt:"+atomInt);
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[10];
        for (int i = 0; i <threads.length ; i++) {
            threads[i]=new D13VolatileTest();
            threads[i].start();
            /*法3:join().但線程會變成穿行執行,不會再出現共享變量不一致。*/
            /*try {
                threads[i].join();
            } catch (InterruptedException e) {
                System.out.println(e);
            }*/
            System.out.println(threads[i].isDaemon());

        }
        /*子線程會在mian線程退出時退出,無論子線程是否執行完畢,解決方法:
        * 法1.判斷線程組的線程數*/
        /*while (Thread.activeCount() > 2) {
            Thread.yield();
        }*/
        /*法2:主線程等待*/
        try {
            Thread.sleep(5_000);
        } catch (InterruptedException e) {
            System.out.println(e);
        }

        D13VolatileTest.get();
    }
}

多次執行:

volInt:94555; normalInt:98774; atomInt:100000
volInt:89376; normalInt:95213; atomInt:100000
volInt:83797; normalInt:89708; atomInt:100000

可見:
1)volatile不能保證線程安全。
2)volatile變量不一定比普通變量更安全。

參考:
徹底理解volatile關鍵字

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章