- CAS(Compare And Swap)
CAS(Compare And Swap),即比较并交换,是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS包含三个操作数
- 更新的变量(V)
- 预期原值(E)
- 新值(N)
核心算法是如果V值等于E值,则将V值设为N值,若V值和E值不同,则说明已经有其他线程做了更新,当前线程则不做更新,直到V值、E值相等,然后才更新V值。
示例:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class CASTest {
public static void main(String[] args) throws InterruptedException {
AtomicInteger counter = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(10000);
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.print(counter.incrementAndGet() + " ");
latch.countDown();
}
}).start();
}
// 一直等待则不会打印AtomicInteger值
// latch.await();
System.out.println("AtomicInteger : " + counter.get());
}
}
- CAS中的ABA问题
ABA问题是指假设当前值为A,如果另一个线程将A修改成B,然后再修改回A,当前线程的CAS操作无法分辨当前值发生过变化。ABA是不是一个问题与程序的逻辑有关,一般不是问题。而如果确实有问题,解决方法是使用AtomicStampedReference,在修改值的同时附加一个时间戳,只有值和时间戳都相同才进行修改。
代码示例:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABATest {
public static void main(String[] args) throws InterruptedException {
CASABATest();
atomicStampedReferenceTest();
}
public static void atomicStampedReferenceTest() {
// 第一个参数:引用对象的初始值,第二个参数:这个对象的初始标记
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0);
new Thread(new Runnable() {
@Override
public void run() {
// compareAndSet(eR,nR,eS,nS) eR:引用对象的期望值,nR:引用对象的新值,eS:引用对象期望标记,nS:引用对象的新标记,当 eR、eS和当前线程的值相等才会修改eR、eS的值为nR、nS
System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
}
}, "Thread A").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(1, 0, 1, 2) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
}
}, "Thread B").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
}
}, "Thread C").start();
}
/**
* 演示CAS 更新的ABA 问题
*/
public static void CASABATest() throws InterruptedException {
AtomicInteger integer = new AtomicInteger(0);
new Thread(new Runnable() {
@Override
public void run() {
// compareAndSet(e,u) e:期望值,u:新值,如果e的值在当前线程中和integer的值一致,就把e的值修改成u的值
System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(0, 1) + ", AtomicInteger value:" + integer); //true
}
}, "Thread A").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(1, 0) + ", AtomicInteger value:" + integer); //true
}
}, "Thread B").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(0, 1) + ", AtomicInteger value:" + integer); //true
}
}, "Thread C").start();
}
}
2.3 CAS 与 synchronized 对比
synchronized 是悲观的,它假设更新都是可能冲突的,所以要先获取锁,得到锁才更新,它是阻塞式算法,得不到锁就进入锁池等待。
CAS 是乐观的,它假设冲突比较少,但使用CAS 更新,进行冲突检测,如果确实冲突就继续尝试直到成功,它是非阻塞式算法,有更新冲突就重试。