天天看点

使用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

<code>import</code> <code>java.util.concurrent.CountDownLatch;</code>

<code>import</code> <code>java.util.concurrent.atomic.AtomicInteger;</code>

<code>public</code> <code>class</code> <code>TestVolatile {</code>

<code>    </code><code>private</code> <code>static</code> <code>int</code> <code>CALC_TIME = </code><code>1000</code><code>;</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>int</code> <code>THREAD_NUM = </code><code>100</code><code>;</code>

<code>    </code><code>private</code> <code>AtomicInteger ai;</code>

<code>    </code><code>private</code> <code>int</code> <code>i;</code>

<code>    </code><code>private</code> <code>volatile</code> <code>int</code> <code>vi;</code>

<code>                                                                                                                                                                                                          </code> 

<code>    </code><code>public</code> <code>TestVolatile(){</code>

<code>        </code><code>ai = </code><code>new</code> <code>AtomicInteger(</code><code>0</code><code>);</code>

<code>        </code><code>i = </code><code>0</code><code>;</code>

<code>        </code><code>vi = </code><code>0</code><code>;</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) </code><code>throws</code> <code>InterruptedException {</code>

<code>        </code><code>System.out.println(</code><code>"Calculation Times:"</code> <code>+ CALC_TIME + </code><code>" ----------------------"</code><code>);</code>

<code>        </code><code>test();</code>

<code>                                                                                                                                                                                                              </code> 

<code>        </code><code>CALC_TIME = </code><code>10000</code><code>;</code>

<code>        </code><code>CALC_TIME = </code><code>100000</code><code>;</code>

<code>        </code><code>CALC_TIME = </code><code>1000000</code><code>;</code>

<code>    </code><code>private</code> <code>static</code> <code>void</code> <code>test() </code><code>throws</code> <code>InterruptedException {</code>

<code>        </code><code>testAi();</code>

<code>        </code><code>testI();</code>

<code>        </code><code>testVi();</code>

<code>    </code><code>private</code> <code>static</code> <code>void</code> <code>testAi() </code><code>throws</code> <code>InterruptedException {</code>

<code>        </code><code>TestVolatile testVolatile = </code><code>new</code> <code>TestVolatile();</code>

<code>        </code><code>CountDownLatch begSignal = </code><code>new</code> <code>CountDownLatch(</code><code>1</code><code>);</code>

<code>        </code><code>CountDownLatch endSignal = </code><code>new</code> <code>CountDownLatch(THREAD_NUM);</code>

<code>        </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; THREAD_NUM; i++) {       </code>

<code>            </code><code>new</code> <code>Thread( testVolatile.</code><code>new</code> <code>WorkerAI(begSignal, endSignal) ).start();</code>

<code>        </code><code>}</code>

<code>        </code><code>long</code> <code>startTime = System.currentTimeMillis();</code>

<code>        </code><code>begSignal.countDown();</code>

<code>        </code><code>endSignal.await();</code>

<code>        </code><code>long</code> <code>endTime = System.currentTimeMillis();</code>

<code>        </code><code>System.out.println(</code><code>"Total time consumed by atomic increment : "</code> <code>+ (endTime-startTime));</code>

<code>    </code><code>private</code> <code>static</code> <code>void</code> <code>testI()</code>

<code>            </code><code>throws</code> <code>InterruptedException {</code>

<code>            </code><code>new</code> <code>Thread( testVolatile.</code><code>new</code> <code>WorkerI(begSignal, endSignal) ).start();</code>

<code>        </code><code>System.out.println(</code><code>"Total time consumed by synchronized increment : "</code> <code>+ (endTime-startTime));</code>

<code>    </code><code>private</code> <code>static</code> <code>void</code> <code>testVi()</code>

<code>            </code><code>new</code> <code>Thread( testVolatile.</code><code>new</code> <code>WorkerVI(begSignal, endSignal) ).start();</code>

<code>        </code><code>System.out.println(</code><code>"Total time consumed by volatile increment : "</code> <code>+ (endTime-startTime));</code>

<code>    </code><code>public</code> <code>void</code> <code>incrAi() {</code>

<code>        </code><code>ai.getAndIncrement();</code>

<code>    </code><code>public</code> <code>synchronized</code> <code>void</code> <code>incrI() {</code>

<code>        </code><code>i++;</code>

<code>    </code><code>/**</code>

<code>     </code><code>* 这个函数不是线程安全,很可能得到错误的结果,这里只是为了测试读取volatile变量的效率</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>void</code> <code>incrVi() {</code>

<code>        </code><code>vi++;</code>

<code>    </code><code>class</code> <code>WorkerAI </code><code>implements</code> <code>Runnable {</code>

<code>        </code><code>private</code> <code>CountDownLatch beginSignal;</code>

<code>        </code><code>private</code> <code>CountDownLatch endSignal;</code>

<code>        </code><code>public</code> <code>WorkerAI(CountDownLatch begin, CountDownLatch end) {</code>

<code>            </code><code>this</code><code>.beginSignal = begin;</code>

<code>            </code><code>this</code><code>.endSignal = end;</code>

<code>        </code><code>@Override</code>

<code>        </code><code>public</code> <code>void</code> <code>run() {</code>

<code>            </code><code>try</code> <code>{</code>

<code>                </code><code>beginSignal.await();</code>

<code>            </code><code>} </code><code>catch</code> <code>(InterruptedException e) {</code>

<code>                </code><code>e.printStackTrace();</code>

<code>            </code><code>}</code>

<code>            </code><code>for</code><code>(</code><code>int</code> <code>j=</code><code>0</code><code>; j&lt;CALC_TIME; j++){</code>

<code>                </code><code>incrAi();</code>

<code>                                                                                                                                                                                                                  </code> 

<code>            </code><code>endSignal.countDown();</code>

<code>    </code><code>class</code> <code>WorkerI </code><code>implements</code> <code>Runnable {</code>

<code>        </code><code>public</code> <code>WorkerI(CountDownLatch begin, CountDownLatch end) {</code>

<code>            </code><code>}       </code>

<code>    </code><code>class</code> <code>WorkerVI </code><code>implements</code> <code>Runnable {</code>

<code>        </code><code>public</code> <code>WorkerVI(CountDownLatch begin, CountDownLatch end) {</code>

<code>                </code><code>incrVi();</code>

<code>}</code>

程序运行结果:

<code>Calculation Times:</code><code>1000</code> <code>----------------------</code>

<code>Total time consumed by atomic increment : </code><code>8</code>

<code>Total time consumed by synchronized increment : </code><code>6</code>

<code>Total time consumed by volatile increment : </code><code>5</code>

<code>Calculation Times:</code><code>10000</code> <code>----------------------</code>

<code>Total time consumed by atomic increment : </code><code>23</code>

<code>Total time consumed by synchronized increment : </code><code>24</code>

<code>Total time consumed by volatile increment : </code><code>15</code>

<code>Calculation Times:</code><code>100000</code> <code>----------------------</code>

<code>Total time consumed by atomic increment : </code><code>354</code>

<code>Total time consumed by synchronized increment : </code><code>360</code>

<code>Total time consumed by volatile increment : </code><code>148</code>

<code>Calculation Times:</code><code>1000000</code> <code>----------------------</code>

<code>Total time consumed by atomic increment : </code><code>3579</code>

<code>Total time consumed by synchronized increment : </code><code>3608</code>

<code>Total time consumed by volatile increment : </code><code>1519</code>

(怀疑自己的程序写得有问题,但暂时找不到问题,请大家帮忙拍砖!)

从测试结果看,原子变量的效率与synchronized同步操作效率差不多,感觉不到优势,volatile变量提升一倍的性能(当然++操作是有同步问题),所以如果volatile变量能满足需求优先使用volatile变量,原子变量次之。那什么时候适合使用volatile变量?专家推荐最佳实践是同时满足以下三个条件:

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

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

在访问变量时不需要加锁

个人实践总结:

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

     本文转自sarchitect 51CTO博客,原文链接:http://blog.51cto.com/stevex/1285964,如需转载请自行联系原作者

继续阅读