[英]Is it necessary to make a primitive instance variable volatile?
為了試驗多線程概念,我正在實現我自己的使用悲觀鎖定的AtomicInteger版本。 它看起來像這樣:
public class ThreadSafeInt {
public int i; // Should this be volatile?
public ThreadSafeInt(int i) {
this.i = i;
}
public synchronized int get() {
return i;
}
public synchronized int getAndIncrement() {
return this.i++;
}
// other synchronized methods for incrementAndGet(), etc...
}
我編寫了一個測試,它接受一個ThreadSafeInt實例,將它提供給數百個線程,並使每個線程調用getAndIncrement 100,000次。 我所看到的是所有增量都正確發生,整數的值正好(number of threads) * (number of increments per thread)
,即使我沒有在原始實例變量i
上使用volatile。 我期望如果我沒有使i
易變,那么我會得到很多可見性問題,例如,線程1將i
從0遞增到1,但是線程2仍然看到值0並且還將其遞增到1
,導致最終值小於正確值。
我知道可見性問題是隨機發生的,並且可能取決於我的環境的屬性,因此即使可見性問題存在固有的潛在可能性,我的測試也可以正常工作。 所以我傾向於認為volatile關鍵字仍然是必要的。
但這是對的嗎? 或者我的代碼有一些屬性(可能是它只是一個原始變量等),我實際上可以信任它來消除對volatile關鍵字的需求?
即使我沒有在原始實例變量i上使用volatile。 我預計,如果我沒有讓我變得不穩定,那么我會遇到很多可見性問題
通過使getAndIncrement()
和get()
方法synchronized
,正在修改i
所有線程都正確地鎖定它以進行更新和值的檢索。 synchronized
塊使得i
不必變得volatile
因為它們也確保了內存同步。
也就是說,您應該使用AtomicInteger
來包裝volatile int
字段。 AtomicInteger
getAndIncrement()
方法更新該值,而不必求助於synchronized
塊,這仍然是線程安全的更快。
public final AtomicInteger i = new AtomicInteger();
...
// no need for synchronized here
public int get() {
return i.get();
}
// nor here
public int getAndIncrement() {
return i.getAndIncrement();
}
我會得到很多可見性問題,例如,線程1將i從0遞增到1,但是線程2仍然看到值0並且還將其遞增到1,導致最終值小於正確值。
如果你的get()
方法沒有synchronized
那么你的增量可能會被正確處理,但是其他線程看不到i
正確發布的值。 但是這兩種方法都是synchronized
這確保了讀寫時的內存同步。 synchronized
也會執行鎖定,以便您可以執行i++
。 AtomicInteger
再次更有效地處理內存同步和增量競爭條件。
更具體地說,當進入synchronized
塊時,它與讀取存儲器屏障相交,該讀取存儲器屏障與從volatile
字段讀取相同。 當退出synchronized
塊時,它會越過寫入內存屏障,這與寫入volatile
字段相同。 與synchronized
塊的不同之處在於還存在鎖定以確保一次只有一個人鎖定特定對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.