使用Volatile变量还是原子变量

  • 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布尔变量,其他数据类型使用原子变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值