CAS (自旋锁 优化)
CAS的全称是 compare and swap,他是同步类的基础, javade concurrent中的原子性都是通过CAS进行实现的
**
jdk8
**中底层调用的native方法是Unsafe文件中的compareAndSwap
jdk11
中有了升级调用的native方法是 Unsafe文件中weakCompareAndSet
原理
按照理解,因为是自旋锁优化,实际不是上锁就是在某个线程在改变volatile之前需要验证一下,要改变的值和预期的值是否一致,如果一致则进行变更。通过这种方式保证了volatile变量的原子性。
/**
* Atomically adds the given value to the current value of a field
* or array element within the given object {@code o}
* at the given {@code offset}.
*
* @param o object/array to update the field/element in
* @param offset field/element offset
* @param delta the value to add
* @return the previous value
* @since 1.8
*/
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
// 现获取到当前线程共享的 Volatile
v = getIntVolatile(o, offset);
/**
o: 代表的是volatile的变量
v: 在改变过程中预期的值
v + delta: 改变后的值
*/
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
ABA问题
当一个值原本被一个线程读到,准备通过CAS修改的时候,突然因为线程卡住,第二个线程把期望的值改成其他的值后,随后把所期望的值修改回去。第一线程是没有正常执行是无法察觉到的
public class ABA {
// 模拟两个线程
static volatile AtomicInteger a = new AtomicInteger(0);
public static void main(String[] args) {
final Thread t1 = new Thread(() -> {
int exceptVal = a.get();
System.out.println(Thread.currentThread().getName() + " 第一次取到的值: " + exceptVal);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 是否修改成功: " + a.compareAndSet(exceptVal, exceptVal + 1));
}, "t1");
final Thread t2 = new Thread(() -> {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.incrementAndGet();
System.out.println(Thread.currentThread().getName() + "increment之后的值:" + a.get());
a.decrementAndGet();
System.out.println(Thread.currentThread().getName() + "decrement之后的值:" + a.get());
}, "t2");
t1.start();
t2.start();
}
}
ABA问题的解决
上述的问题jdk提供了解决方案,通过引入版本号来解决问题 AtomicStampedReference
public class ABA {
// 模拟两个线程
// static volatile AtomicInteger a = new AtomicInteger(0);
static volatile AtomicStampedReference<Integer> a = new AtomicStampedReference<>(0,1);
public static void main(String[] args) {
final Thread t1 = new Thread(() -> {
int exceptVal = a.getReference();
int exceptStamp = a.getStamp();
System.out.println(Thread.currentThread().getName() + " 第一次取到的值: " + exceptVal);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 是否修改成功: " + a.compareAndSet(exceptVal, exceptVal + 1,exceptStamp,exceptStamp + 1));
System.out.println("版本号Stamp:" + a.getStamp());
}, "t1");
final Thread t2 = new Thread(() -> {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.compareAndSet(a.getReference(), a.getReference() + 1, a.getStamp(), a.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "increment之后的值:" + a.getReference());
a.compareAndSet(a.getReference(), a.getReference() - 1, a.getStamp(), a.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "decrement之后的值:" + a.getReference());
}, "t2");
t1.start();
t2.start();
}
}