八、非阻塞操作CAS
8.1、锁的劣势
不管是内置锁synchronized还是显示锁Lock,在一个线程获取到锁之后,另一个线程再去尝试获取锁的时候,都会因为无法获取到锁而进入阻塞状态。当获取锁的线程释放锁之后,其余在阻塞状态下的线程才能获取到锁。
8.2、CompareAndSwap
java中的锁是悲观的技术,在操作之前总是认为会有其他线程会干预自己的操作,所有对需要操作的资源进行加锁。而CAS是一种乐观的技术。
CompareAndSwap(CAS)有一种冲突检查机制,在进行操作的时候会检查之前获取到的变量的值和现在正在操作的变量的值是否一致。CompareAndSwap的意思就是先比较再更新,这种先比较再更新的操作是原子性的,他的原子性是由底层硬件来保证的。
CAS操作需要3个参数:
(1)需要更新的变量V
(2)变量之前的值A
(3)变量即将更新的值B
CAS操作就是:如果V的值等于A的话,就将B赋值给V并返回true;如果V的值不等于A的话,就不做更新并返回false。
8.3、Unsafe
// TODO
8.3、原子操作变量
根据CAS机制,java提供了多种原子更新类,可以以原子的方式更新变量的值,在多线程中即保证了安全又保证了性能。
8.3.1、原子更新基本类型
AtomicBoolean、AtomicInteger、AtomicLong
使用举例:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
public static void main(String[] args) throws Exception {
test(100, 10);
test(100, 100);
test(100, 1000);
test(100, 10000);
test(100, 100000);
test(100, 1000000);
}
private static void test(int threadCount, int incrementCount) throws Exception {
Increment increment = new Increment();
Thread[] threads = new Thread[threadCount];
for(int i = 0; i < threadCount; i++) {
Thread thread = new Thread(() -> {
for(int j = 0; j < incrementCount; j++) {
increment.increment();
}
});
threads[i] = thread;
thread.start();
}
for(int i = 0; i < threadCount; i++) {
threads[i].join();
}
System.out.println(increment.getCount());
}
}
class Increment{
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public Integer getCount() {
return count.get();
}
}
运行结果:
1000
10000
100000
1000000
10000000
100000000
8.3.2、原子更新引用类型
AtomicReference、AtomicStampedReference、AtomicMarkableReference
使用举例:
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceDemo {
public static void main(String[] args) {
Object obj1 = new Object();
AtomicReference<Object> atomicReference = new AtomicReference<>(obj1);
Object obj2 = new Object();
boolean cas1 = atomicReference.compareAndSet(obj1, obj2);
System.out.println("第一次cas结果:" + cas1);
Object obj3 = new Object();
boolean cas2 = atomicReference.compareAndSet(obj1, obj3);
System.out.println("第二次cas结果:" + cas2);
}
}
运行结果:
第一次cas结果:true
第二次cas结果:false
8.3.3、原子更新数组
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
使用举例:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] arr = new int[]{0, 1, 2, 3, 4, 5};
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
int index = 0;
int value = atomicIntegerArray.get(index);
System.out.println("数组中角标为" + index + "的元素的值为:" + value);
int update = 1;
boolean cas1 = atomicIntegerArray.compareAndSet(index, value, update);
System.out.println("第一次cas结果:" + cas1);
boolean cas2 = atomicIntegerArray.compareAndSet(index, value, update);
System.out.println("第二次cas结果:" + cas2);
System.out.println("操作完成后数组中角标为" + index + "的元素的值为:" + arr[index]);
System.out.println("操作完成后atomicIntegerArray中角标为" + index + "的元素的值为:" + atomicIntegerArray.get(index));
}
}
运行结果:
数组中角标为0的元素的值为:0
第一次cas结果:true
第二次cas结果:false
操作完成后数组中角标为0的元素的值为:0
操作完成后atomicIntegerArray中角标为0的元素的值为:1
8.3.4、原子更新字段类
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) {
AtomicIntegerFieldUpdater<IntegerWapper> atomicIntegerFieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(IntegerWapper.class, "value");
IntegerWapper integerWapper = new IntegerWapper();
integerWapper.value = 100;
boolean cas1 = atomicIntegerFieldUpdater.compareAndSet(integerWapper, 100, 200);
System.out.println("第一次cas结果:" + cas1);
boolean cas2 = atomicIntegerFieldUpdater.compareAndSet(integerWapper, 100, 200);
System.out.println("第二次cas结果:" + cas2);
System.out.println(atomicIntegerFieldUpdater.get(integerWapper));
System.out.println(integerWapper.value);
}
}
class IntegerWapper{
public volatile int value;
}
运行结果:
第一次cas结果:true
第二次cas结果:false
200
200
注:IntegerWapper类中需要原子更新的字段类型必须是public volatile int修饰的,不能是Integer,否则会报错。
8.4、非阻塞算法
8.5、CAS的问题
(1)ABA问题
(2)循环时间长开销大
(3)只能保证一个共享变量的原子操作