天天看點

使用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,如需轉載請自行聯系原作者

繼續閱讀