怎樣正確理解volatile?
volatile是java虛擬機器提供的輕量級的同步機制
三大特徵:
(1)保證可見性
當執行緒對變數進行操作時,必須在工作記憶體中進行,完成後再寫會到主記憶體中。
在寫回主記憶體中後其他執行緒馬上都能看到,就是 可見性
(2)不保證原子性
丟失寫值的情況
(3)禁止指令重拍
JMM記憶體模型
可見性
原子性
有序性
主記憶體:主記憶體是共享記憶體的區域,相當於我們的記憶體條,裡面儲存共享變數,所有執行緒都能訪問
工作記憶體:每個執行緒建立時JVM都會為其建立一個工作記憶體,工作記憶體裡面存放的是該執行緒的私有資料,不能相互訪問,
當執行緒對變數進行操作時,必須在工作記憶體中進行,完成後再寫會到主記憶體中。
在寫回主記憶體中後其他執行緒馬上都能看到,就是可見性
什麼是CAS(compareAndSet)?
比較並交換
AtomicInteger.compareAndSet(期望值,要修改的值)
期望值:從主實體記憶體中的值
執行緒每次修改值的時候都要從主實體記憶體中去獲取並比較是否和期望值相同,如果和期望值相同,則修改成要修改的值,並返回true
CAS底層原理?
unsafe valueoffset(記憶體偏移地址)
Unsafe是CAS的核心類
atomicInteger.getAndIncrement(); 底層原始碼::::: public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
假設 執行緒A 和 執行緒B 同時執行getAndAddInt操作,
首先執行緒A先從主記憶體中獲取到變數A=3到自己的執行緒記憶體中,
同時執行緒B也從主記憶體中獲取到變數A=3到自己的執行緒記憶體中,
現在AB執行緒中都有一份自己的記憶體變數A=3的副本,
執行緒A透過this.getIntVolatile(var1, var2);
獲取到記憶體中的A=3,這時執行緒A被掛起。
這時執行緒B透過this.getIntVolatile(var1, var2);
獲取到記憶體中的A=3,並執行了this.compareAndSwapInt(var1, var2, var5, var5 + var4)
比較主記憶體中的值也為3,就成功將自己的工作記憶體中的A修改為4並寫回到主記憶體中。
這時執行緒A回覆,執行compareAndSwapIn方法比較,發現自己工作記憶體中的A與主記憶體中的A
不一致,說明已經被其他執行緒修改過了,那執行緒A本次修改失敗,while重新來一遍
執行緒A重新獲取value,因為變數被volatile修飾,所以其他執行緒對他的修改執行緒A總是能看到
(volatile的可見性原理)執行緒A繼續執行compareAndSwapInt進行比較直到成功
缺點:
迴圈時間開銷很大
只能保證一個共享變數的原子操作
ABA問題(狸貓換太子)
CAS演算法是提取記憶體中某時刻的資料並在當下時刻進行比較並替換,這個時間差會導致資料變化
比如T1從記憶體位置V中取出A,這時另一個執行緒T2也取出A,T2 將A–>B,然後T2又將B–>A,這
時T1進行CAS發現記憶體中還是A,然後執行緒T1就操作成功了
解決ABA問題:
原子引用AtomicReference
新增一種機制,修改版本號 AtomicStampedReference(初始值,初始版本號)
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946594/viewspace-2655460/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 理解並正確使用synchronized和volatilesynchronized
- 正確理解ThreadLocalthread
- 正確理解CAP理論
- LongTree的正確分析理解
- 正確理解 PHP 的過載PHP
- 如何正確理解棧和堆?
- 正確理解Hibernate Inverse (轉)
- 正確理解BI(商業智慧)
- 怎樣正確運用網路輿情大資料?大資料
- Java理論與實踐:正確使用Volatile變數Java變數
- Java 理論與實踐: 正確使用 Volatile 變數Java變數
- Jtti:怎樣正確處理Redis中的海量資料JttiRedis
- background-position的正確理解方式
- 正確理解memcached,才能更好的使用
- 如何理解並正確使用 MySQL 索引MySql索引
- 正確理解 PHP 錯誤資訊(轉)PHP
- Java理論與實踐:正確使用Volatile變數(轉)Java變數
- 理解volatile
- 談如何正確理解 IP 資料的覆蓋率,兼談正確率~
- 報表應用系統中怎樣正確使用圖表功能
- 怎樣正確設定remote_addr和x_forwarded_forREMForward
- 磨鍊Gentoo的鋒芒之怎樣正確配置網路(轉)
- 如何正確理解「指標」和「標籤」指標
- 怎樣理解 cosocket
- volatile的理解
- Redis 記憶體滿了怎麼辦?這樣設定才正確!Redis記憶體
- 怎樣正確的去選擇蘋果企業簽名?看這裡!蘋果
- 怎樣才算是無線網路擴充套件的正確姿勢?套件
- 正確理解手機智慧作業系統作業系統
- 正確理解專案交付成果(Deliverable)(轉)
- 深入理解volatile
- 徹底理解volatile
- synchronized和volatile理解synchronized
- 如何正確理解Python培訓?有必要嗎?Python
- 正確理解和使用JAVA中的字串常量池Java字串
- 正確理解tnsnames.ora中的service_name
- 怎麼正確釋出dubbo控制檯
- 編寫高質量的js之正確理解正規表示式回溯JS