1. 程式人生 > 其它 >volatile 非原子性

volatile 非原子性

上一章節我們講了 volatile的可見性的,以及可見性的演示,這裡就會給大家產生一個誤區,這樣的使用方式很容易給人的感覺是

對volatile修飾的變數進行併發操作是執行緒安全的。 其實不然,用volatile修飾的變數只有兩個特性就是 可見性、禁止指令重排序。並不能保證執行緒的安全性

我們通過以下程式碼進行演示。

   volatile int iCounter = 0;

    AtomicInteger atomicInteger = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        LatchTest latchTest = new LatchTest();
        latchTest.startTaskAllInOnce(5);
    }

    private void m() throws InterruptedException {
        for (int i = 0; i < 1000000; i++) {
            iCounter++;
            atomicInteger.incrementAndGet();
        }
    }

    public void startTaskAllInOnce(int threadNums) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(threadNums);
        for (int i = 0; i < threadNums; i++) {
            new Thread(() -> {
                try {
                    System.out.println("wait thread");
                    startGate.await();
                    try {
                        m();
                    } finally {
                        endGate.countDown();
                    }
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }).start();
        }
        System.out.println("thread waited");
        startGate.countDown();
        endGate.await();

        System.out.println("iCounter: " + iCounter + " atomicInteger :" + atomicInteger);
    }

輸出結果:
volatile iCounter: 2972488 atomicInteger :5000000
volatile iCounter: 2737312 atomicInteger :5000000
volatile iCounter: 3613868 atomicInteger :5000000
volatile iCounter: 2081604 atomicInteger :5000000
volatile iCounter: 2875711 atomicInteger :5000000
volatile iCounter: 3037079 atomicInteger :5000000
volatile iCounter: 2806466 atomicInteger :5000000
volatile iCounter: 3218029 atomicInteger :5000000
volatile iCounter: 2608899 atomicInteger :5000000
volatile iCounter: 2513628 atomicInteger :5000000

    
AtomicInteger 是原子性操作,執行緒安全的,volatile 並不能保證執行緒安全。
    

這是因為雖然 volatile 保證了記憶體可見性,每個執行緒拿到的值都是最新值,但 count++ 這個操作並不是原子的,這裡面涉及到獲取值、自增、賦值的操作並不能同時完成。

  • 所以想到達到執行緒安全可以使這三個執行緒序列執行(其實就是單執行緒,沒有發揮多執行緒的優勢)。
  • 也可以使用 synchronize 或者是鎖的方式來保證原子性。
  • 還可以用 Atomic 包中 AtomicInteger 來替換 int****,它利用了 CAS 演算法來保證了原子性。