程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Volatile變量還是原子變量

使用Volatile變量還是原子變量

編輯:關於JAVA

volatile變量

在Java語言中,volatile變量提供了一種輕量級的同步機制,volatile變量用來確保將變量的更新操作通知到其它線程,volatile變量不會被緩存到寄存器或者對其它處理器不可見的地方,所以在讀取volatile變量時總會返回最新寫入的值,volatile變量通常用來表示某個狀態標識。

原子變量:

原子變量是“更強大的volatile”變量,從實現來看,每個原子變量類的value屬性都是一個volatile變量,所以volatile變量的特性原子變量也有。同時,原子變量提供讀、改、寫的原子操作,更強大,更符合一般並發場景的需求。

既然原子變量更強大,是否還有必要使用volatile變量?如果有什麼時候選擇volatile變量,什麼時候選擇原子變量?當然這種選擇只有在多線程並發的場景下才會出現,而多線程並發的目的一般是為了提高吞吐量和減少延遲響應,所以還是先看段測試代碼和運行結果吧!

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class TestVolatile {
    private static int CALC_TIME = 1000;
    private static final int THREAD_NUM = 100;
    private AtomicInteger ai;
    private int i;
    private volatile int vi;
                                                                                                                                                                                                               
    public TestVolatile(){
        ai = new AtomicInteger(0);
        i = 0;
        vi = 0;
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
        test();
                                                                                                                                                                                                                   
        CALC_TIME = 10000;
        System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
        test();
                                                                                                                                                                                                                   
        CALC_TIME = 100000;
        System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
        test();
                                                                                                                                                                                                                   
        CALC_TIME = 1000000;
        System.out.println("Calculation Times:" + CALC_TIME + " ----------------------");
        test();
    }
    private static void test() throws InterruptedException {
        testAi();
                                                                                                                                                                                                                   
        testI();
                                                                                                                                                                                                                   
        testVi();
    }
    private static void testAi() throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        CountDownLatch begSignal = new CountDownLatch(1);
        CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
        for (int i = 0; i < THREAD_NUM; i++) {      
            new Thread( testVolatile.new WorkerAI(begSignal, endSignal) ).start();
        }
        long startTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        begSignal.countDown();
        endSignal.await();
                                                                                                                                                                                                                   
        long endTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        System.out.println("Total time consumed by atomic increment : " + (endTime-startTime));
    }
    private static void testI()
            throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        CountDownLatch begSignal = new CountDownLatch(1);
        CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
                                                                                                                                                                                                                   
        for (int i = 0; i < THREAD_NUM; i++) {      
            new Thread( testVolatile.new WorkerI(begSignal, endSignal) ).start();
        }
        long startTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        begSignal.countDown();
        endSignal.await();
                                                                                                                                                                                                                   
        long endTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        System.out.println("Total time consumed by synchronized increment : " + (endTime-startTime));
    }
                                                                                                                                                                                                               
    private static void testVi()
            throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        CountDownLatch begSignal = new CountDownLatch(1);
        CountDownLatch endSignal = new CountDownLatch(THREAD_NUM);
                                                                                                                                                                                                                   
        for (int i = 0; i < THREAD_NUM; i++) {      
            new Thread( testVolatile.new WorkerVI(begSignal, endSignal) ).start();
        }
        long startTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        begSignal.countDown();
        endSignal.await();
                                                                                                                                                                                                                   
        long endTime = System.currentTimeMillis();
                                                                                                                                                                                                                   
        System.out.println("Total time consumed by volatile increment : " + (endTime-startTime));
    }
    public void incrAi() {
        ai.getAndIncrement();
    }
    public synchronized void incrI() {
        i++;
    }
    /**
     * 這個函數不是線程安全,很可能得到錯誤的結果,這裡只是為了測試讀取volatile變量的效率
     */
    public void incrVi() {
        vi++;
    }
    class WorkerAI implements Runnable {
        private CountDownLatch beginSignal;
        private CountDownLatch endSignal;
        public WorkerAI(CountDownLatch begin, CountDownLatch end) {
            this.beginSignal = begin;
            this.endSignal = end;
        }
        @Override
        public void run() {
            try {
                beginSignal.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int j=0; j<CALC_TIME; j++){
                incrAi();
            }
                                                                                                                                                                                                                       
            endSignal.countDown();
        }
    }
    class WorkerI implements Runnable {
        private CountDownLatch beginSignal;
        private CountDownLatch endSignal;
        public WorkerI(CountDownLatch begin, CountDownLatch end) {
            this.beginSignal = begin;
            this.endSignal = end;
        }
        @Override
        public void run() {
            try {
                beginSignal.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int j=0; j<CALC_TIME; j++){
                incrAi();
            }      
            endSignal.countDown();
        }
    }
    class WorkerVI implements Runnable {
        private CountDownLatch beginSignal;
        private CountDownLatch endSignal;
        public WorkerVI(CountDownLatch begin, CountDownLatch end) {
            this.beginSignal = begin;
            this.endSignal = end;
        }
        @Override
        public void run() {
            try {
                beginSignal.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int j=0; j<CALC_TIME; j++){
                incrVi();
            }
            endSignal.countDown();
        }
    }
}

程序運行結果:

Calculation Times:1000 ----------------------
Total time consumed by atomic increment : 8
Total time consumed by synchronized increment : 6
Total time consumed by volatile increment : 5
Calculation Times:10000 ----------------------
Total time consumed by atomic increment : 23
Total time consumed by synchronized increment : 24
Total time consumed by volatile increment : 15
Calculation Times:100000 ----------------------
Total time consumed by atomic increment : 354
Total time consumed by synchronized increment : 360
Total time consumed by volatile increment : 148
Calculation Times:1000000 ----------------------
Total time consumed by atomic increment : 3579
Total time consumed by synchronized increment : 3608
Total time consumed by volatile increment : 1519

(懷疑自己的程序寫得有問題,但暫時找不到問題,請大家幫忙拍磚!)

從測試結果看,原子變量的效率與synchronized同步操作效率差不多,感覺不到優勢,volatile變量提升一倍的性能(當然++操作是有同步問題),所以如果volatile變量能滿足需求優先使用volatile變量,原子變量次之。那什麼時候適合使用volatile變量?專家推薦最佳實踐是同時滿足以下三個條件:

對變量的寫入操作不依賴變量的當前值,或者能確保只有單個線程更新變量的值

改變量不會與其他狀態變量一起組成不變性的條件

在訪問變量時不需要加鎖

個人實踐總結:

滿足條件的情況下使用volatile布爾變量,其他數據類型使用原子變量。

出處:http://stevex.blog.51cto.com/4300375/1285964

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved