一豹休、什么是CAS炊昆?
CAS,全稱Compare And Swap(比較與交換)威根,解決多線程并行情況下使用鎖造成性能損耗的一種機制窑眯。
CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)医窿。 如果內存位置的值與預期原值相匹配磅甩,那么處理器會自動將該位置值更新為新值 。否則姥卢,處理器不做任何操作卷要。無論哪種情況,它都會在 CAS 指令之前返回該 位置的值
二独榴、CAS的目的
利用CPU的CAS指令僧叉,同時借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的棺榔。而整個J.U.C都是建立在CAS之上的瓶堕,因此對于synchronized阻塞算法,J.U.C在性能上有了很大的提升症歇。
三郎笆、CAS(compareAndSwap)的原理探究
CAS的實現(xiàn)主要在JUC中的atomic包,我們以AtomicInteger類為例:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
通過代碼追溯忘晤,可以看出JAVA中的CAS操作都是通過sun包下Unsafe類實現(xiàn)宛蚓,而Unsafe類中的方法都是native方法,由JVM本地實現(xiàn)设塔,所以最終的實現(xiàn)是基于C凄吏、C++在操作系統(tǒng)之上操作
Unsafe類
java不能直接訪問操作系統(tǒng)底層,而是通過本地方法來訪問闰蛔。Unsafe類提供了硬件級別的原子操作痕钢,主要提供了以下功能:
1、通過Unsafe類可以分配內存序六,可以釋放內存任连;
類中提供的3個本地方法allocateMemory、reallocateMemory难咕、freeMemory分別用于分配內存课梳,擴充內存和釋放內存距辆,與C語言中的3個方法對應余佃。
2暮刃、可以定位對象某字段的內存位置,也可以修改對象的字段值爆土,即使它是私有的椭懊;
3、掛起與恢復
將一個線程進行掛起是通過park方法實現(xiàn)的步势,調用 park后氧猬,線程將一直阻塞直到超時或者中斷等條件出現(xiàn)。unpark可以終止一個掛起的線程坏瘩,使其恢復正常盅抚。整個并發(fā)框架中對線程的掛起操作被封裝在 LockSupport類中,LockSupport類中有各種版本pack方法倔矾,但最終都調用了Unsafe.park()方法妄均。
public class LockSupport {
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(true, deadline);
setBlocker(t, null);
}
public static void park() {
unsafe.park(false, 0L);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
unsafe.park(false, nanos);
}
public static void parkUntil(long deadline) {
unsafe.park(true, deadline);
}
}
4、CAS操作
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapLong(Object o, long offset,
long expected,
long x);
四哪自、CAS機制的優(yōu)缺點
4.1 優(yōu)點
CAS是一種樂觀鎖丰包,而且是一種非阻塞的輕量級的樂觀鎖,什么是非阻塞式的呢壤巷?其實就是一個線程想要獲得鎖邑彪,對方會給一個回應表示這個鎖能不能獲得。在資源競爭不激烈的情況下性能高胧华,相比synchronized重量鎖寄症,synchronized會進行比較復雜的加鎖,解鎖和喚醒操作矩动。
4.2 缺點
1)循環(huán)時間長開銷大瘸爽,占用CPU資源
2)只能保證一個共享變量的原子操作
3)ABA問題
如線程1從內存X中取出A,這時候另一個線程2也從內存X中取出A铅忿,并且線程2進行了一些操作將內存X中的值變成了B剪决,然后線程2又將內存X中的數據變成A,這時候線程1進行CAS操作發(fā)現(xiàn)內存X中仍然是A檀训,然后線程1操作成功柑潦。雖然線程1的CAS操作成功,但是整個過程就是有問題的峻凫。比如鏈表的頭在變化了兩次后恢復了原值渗鬼,但是不代表鏈表就沒有變化。
ABA問題解決方案:
1荧琼、添加版本號
2譬胎、AtomicStampedReference
java并發(fā)包為了解決這個問題差牛,提供了一個帶有標記的原子引用類“AtomicStampedReference”,它可以通過控制變量值的版本來保證CAS的正確性堰乔。因此偏化,在使用CAS前要考慮清楚“ABA”問題是否會影響程序并發(fā)的正確性,如果需要解決ABA問題镐侯,改用傳統(tǒng)的互斥同步可能會比原子類更高效侦讨。