volatile原理与技巧
轉自http://www.iteye.com/topic/109150
volatile, 用更低的代價替代同步
為什么 使用volatile比同步代價更低??
同步的代價, 主要由其覆蓋范圍決定, 如果可以降低同步的覆蓋范圍, 則可以大幅提升程序性能.
而volatile的覆蓋范圍僅僅變量級別的. 因此它的同步代價很低.
?
volatile原理是什么?
volatile的語義, 其實是告訴處理器, 不要將我放入工作內存, 請直接在主存操作我.(工作內存詳見java內存模型)
因此, 當多核或多線程在訪問該變量時, 都將直接 操作 主存, 這從本質上, 做到了變量共享.
?
volatile的有什么優勢??
1, 更大的程序吞吐量
2, 更少的代碼實現多線程
3, 程序的伸縮性較好
4, 比較好理解, 無需太高的學習成本
?
volatile有什么劣勢??
1, 容易出問題
2, 比較難設計
?
volatile運算存在臟數據問題
volatile僅僅能保證變量可見性, 無法保證原子性.
?
volatile的race condition示例:
Java代碼???
當多線程執行increase方法時, 是否能保證它的值會是線性遞增的呢??
答案是否定的.
?
原因:
這里的increase方法, 執行的操作是i++, 即 i = i + 1;
針對i = i + 1, 在多線程中的運算, 本身需要改變i的值.
如果, 在i已從內存中取到最新值, 但未與1進行運算, 此時其他線程已數次將運算結果賦值給i.
則當前線程結束時, 之前的數次運算結果都將被覆蓋.
即, 執行100次increase, 可能結果是 < 100.
一般來說, 這種情況需要較高的壓力與并發情況下, 才會出現.
?
如何避免這種情況??
解決以上問題的方法:
一種是 操作時, 加上同步.
這種方法, 無疑將大大降低程序性能, 且違背了volatile的初衷.
?
第二種方式是, 使用硬件原語(CAS), 實現非阻塞算法
從CPU原語上, 支持變量級別的低開銷同步.
?
CPU原語-比較并交換(CompareAndSet),實現非阻塞算法
什么是CAS??
cas是現代CPU提供給并發程序使用的原語操作. 不同的CPU有不同的使用規范.
在 Intel 處理器中,比較并交換通過指令的 cmpxchg 系列實現。
PowerPC 處理器有一對名為“加載并保留”和“條件存儲”的指令,它們實現相同的目地;
MIPS 與 PowerPC 處理器相似,除了第一個指令稱為“加載鏈接”。
CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)
?
什么是非阻塞算法??
一個線程的失敗或掛起不應該影響其他線程的失敗或掛起.這類算法稱之為非阻塞(nonblocking)算法
對比阻塞算法:
如果有一類并發操作, 其中一個線程優先得到對象監視器的鎖, 當其他線程到達同步邊界時, 就會被阻塞.
直到前一個線程釋放掉鎖后, 才可以繼續競爭對象鎖.(當然,這里的競爭也可是公平的, 按先來后到的次序)
?
CAS 原理:
我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。
?
CAS使用示例(jdk 1.5 并發包 AtomicInteger類分析:)
?
Java代碼???
這個方法是, AtomicInteger類的常用方法, 作用是, 將變量設置為指定值, 并返回設置前的值.
它利用了cpu原語compareAndSet來保障值的唯一性.
另, AtomicInteger類中, 其他的實用方法, 也是基于同樣的實現方式.
比如 getAndIncrement, getAndDecrement, getAndAdd等等.
?
CAS語義上存在的 " ABA 問題"
什么是ABA問題?
假設, 第一次讀取V地址的A值, 然后通過CAS來判斷V地址的值是否仍舊為A, 如果是, 就將B的值寫入V地址,覆蓋A值.
但是, 語義上, 有一個漏洞, 當第一次讀取V的A值, 此時, 內存V的值變為B值, 然后在未執行CAS前, 又變回了A值.
此時, CAS再執行時, 會判斷其正確的, 并進行賦值.
?
這種判斷值的方式來斷定內存是否被修改過, 針對某些問題, 是不適用的.
為了解決這種問題, jdk 1.5并發包提供了AtomicStampedReference(有標記的原子引用)類, 通過控制變量值的版本來保證CAS正確性.
?
其實, 大部分通過值的變化來CAS, 已經夠用了.
?
jdk1.5原子包介紹(基于volatile)
包的特色:
1, 普通原子數值類型AtomicInteger, AtomicLong提供一些原子操作的加減運算.
2, 使用了解決臟數據問題的經典模式-"比對后設定", 即 查看主存中數據是否與預期提供的值一致,如果一致,才更新.
3, 使用AtomicReference可以實現對所有對象的原子引用及賦值.包括Double與Float,
但不包括對其的計算.浮點的計算,只能依靠同步關鍵字或Lock接口來實現了.
4, 對數組元素里的對象,符合以上特點的, 也可采用原子操作.包里提供了一些數組原子操作類
AtomicIntegerArray, AtomicLongArray等等.
5, 大幅度提升系統吞吐量及性能.
?
具體使用, 詳解java doc.
轉載于:https://www.cnblogs.com/balaamwe/archive/2011/11/22/2259081.html
總結
以上是生活随笔為你收集整理的volatile原理与技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 鱼相忘于江湖,人相忘于道术
- 下一篇: 回发或回调参数无效。下拉菜单中使用aja