1.簡介
JUC 包提供 了一系列的原子性操作類,這些類都是使用非阻塞算法 CAS 現的 ,相 比使用鎖 現原 性操作這在性能上有很大提高。由於原子性操作類的原理都大致相同。
CAS原理:
在Java發展初期,java語言是不能夠利用硬件提供的這些便利來提升系統的性能的。而隨着java不斷的發展,Java本地方法(JNI)的出現,使得java程序越過JVM直接調用本地方法提供了一種便捷的方式,因而java在併發的手段上也多了起來。而在Doug Lea提供的cucurenct包中,CAS理論是它實現整個java包的基石。
CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)。 如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值 。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該 位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前 值。)CAS 有效地說明了“我認爲位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”
通常將 CAS 用於同步的方式是從地址 V 讀取值 A,執行多步計算來獲得新 值 B,然後使用 CAS 將 V 的值從 A 改爲 B。如果 V 處的值尚未同時更改,則 CAS 操作成功。
類似於 CAS 的指令允許算法執行讀-修改-寫操作,而無需害怕其他線程同時 修改變量,因爲如果其他線程修改變量,那麼 CAS 會檢測它(並失敗),算法 可以對該操作重新計算。
CAS失敗測試:100線程競爭更新,只會有一個成功;
public class AtomicIntegerTest {
private static AtomicLong atomicLong = new AtomicLong();
private final static Integer THREAD_NUM = 100;
public static void main(String[] argv) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
for (int i = 0; i < THREAD_NUM; ++i) {
int finalI = i;
int finalI1 = i;
new Thread(new Runnable() {
@Override
public void run() {
boolean compareAndSet = atomicLong.compareAndSet(0, finalI1 + 1);
System.out.println(compareAndSet);
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
System.out.println(atomicLong.get());
}
}
2.測試
兩個線程統計兩個數組中0的個數,結果爲10,沒有出現其他異常情況;驗證了其線程安全性;
public class AtomicIntegerTest {
private static AtomicLong atomicLong = new AtomicLong();
private static Integer[] array1 = new Integer[]{1, 2, 0, 1, 0, 0, 0, 0, 0};
private static Integer[] array2 = new Integer[]{1, 0, 0, 0, 0, 1, 12, 3, 4};
private static void incrementValueCountNum(Integer[] array, int targetValue) {
// System.out.println(Arrays.asList(array1).stream().filter(value->value == 0).count());
for (int i = 0; i < array.length; ++i) {
if (array[i].intValue() == targetValue) {
atomicLong.incrementAndGet();
}
}
}
public static void main(String[] argv) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
incrementValueCountNum(array1, 0);
countDownLatch.countDown();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
incrementValueCountNum(array2, 0);
countDownLatch.countDown();
}
}).start();
countDownLatch.await();
System.out.println(atomicLong.get());
}
}
3.Atmoic-IntegerLong源碼解析
基本屬性:
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
// 主要依賴unsafe來實現CAS
// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
value字段在AtomicLong內的偏移量
private static final long valueOffset;
/**
* Records whether the underlying JVM supports lockless
* compareAndSwap for longs. While the Unsafe.compareAndSwapLong
* method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks.
*/
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
/**
* Returns whether underlying JVM supports lockless CompareAndSet
* for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
*/
private static native boolean VMSupportsCS8();
static {
try {
/// 對valueOffset賦值
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 內部維護的值
private volatile long value;
public final long incrementAndGet():原子上增加一個當前值;返回增加後的值
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
// 自旋更新值,直到成功
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6
}
public final long decrementAndGet():原子減少一個值:返回遞減後的值
// 同遞增方法一樣,加-1L
public final long decrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
public final long addAndGet(long delta):原子加delta,返回增加後的值
同遞增方法
public final long addAndGet(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
public final boolean compareAndSet(long expect, long update) :CAS更新值
// 依賴unsafe類
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
getAndAdd,getAndIncreament,getAndDecrment:返回修改之前的數據,但是數據已經修改:
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final long getAndAdd(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta);
}
4.總結
- atmoic包下的原子類,本質都是依靠CAS算法實現的;
- CAS是用CPU換取上下文開銷的一種方式,因此如果CAS自旋時間過長,存在性能問題,因此jdk提出LongAdder解決這個自旋問題,後續會講;