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解决这个自旋问题,后续会讲;