使用Volatile变量还是原子变量

来源:互联网 发布:mac可以加硬盘吗 编辑:程序博客网 时间:2024/06/11 10:06
  • volatile变量

在Java语言中,volatile变量提供了一种轻量级的同步机制,volatile变量用来确保将变量的更新操作通知到其它线程,volatile变量不会被缓存到寄存器或者对其它处理器不可见的地方,所以在读取volatile变量时总会返回最新写入的值,volatile变量通常用来表示某个状态标识。


  • 原子变量:

原子变量是“更强大的volatile”变量,从实现来看,每个原子变量类的value属性都是一个volatile变量,所以volatile变量的特性原子变量也有。同时,原子变量提供读、改、写的原子操作,更强大,更符合一般并发场景的需求。


既然原子变量更强大,是否还有必要使用volatile变量?如果有什么时候选择volatile变量,什么时候选择原子变量?当然这种选择只有在多线程并发的场景下才会出现,而多线程并发的目的一般是为了提高吞吐量和减少延迟响应,所以还是先看段测试代码和运行结果吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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();
        }
    }
}

程序运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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变量?专家推荐最佳实践是同时满足以下三个条件:

  1. 对变量的写入操作不依赖变量的当前值,或者能确保只有单个线程更新变量的值

  2. 改变量不会与其他状态变量一起组成不变性的条件

  3. 在访问变量时不需要加锁


个人实践总结:

满足条件的情况下使用volatile布尔变量,其他数据类型使用原子变量。

0 0