CAS、自旋锁是什么
CAS即Compare and Swap,是一种比较并交换算法
自旋锁是一种基于CAS的锁,获取锁的线程不会被阻塞,而是循环的去获取锁
CAS的原理
Unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native) 方法来访问,Unsafe相当于一个后门,基于该 类可以直接操作特定内存的数据。Unsafe 类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中 CAS操作的执行依赖于Unsafe类的方法。
CAS有三个操作值:内存值V、预期值A与修改值B。只有当预期值A与内存值V相同时,才会将内存值替换成B。否则,会进入下一轮循环中。
意思就是,假如有一个共享变量O,它的值为1,现在有两个线程A、B去获取修改O,线程A、B获取到O=1,A想要将O修改为2,B想要将O修改为3。A跑得比较快,A拿自己的副本O(值为1)与真实的O(值为1)进行比较,相等,修改成功。然后线程B去将O修改为3,先比较值,B的副本O(值为1)与真实O(值为2)比较,不相等,修改失败。
CAS的优点与缺点
优点:
在并发量不是很高的时候可以提高效率。
缺点:
1、如果长时间循环对CPU的开销很大。
2、只能保证一个共享变量的原子操作
3、ABA问题
ABA问题:
线程A比线程B快得多,此时:
A将O=10修改为20
A将O=20修改为10
B在修改O值时,本应该修改失败,但是因为A将值修改过之后又改回来了,导致B修改成功了,但是在有些业务场景下是不允许这么操作的。
如何解决ABA问题?
对内存中的O值添加一个版本号,在比较值的同时还要比较版本号。
AtomicStampedReference就使用了版本号实现CAS。
自旋锁实例
public class Test1 {
public static void main(String[] args) {
Test1 test1 = new Test1();
new Thread(() -> {
test1.lock();
test1.business();
test1.unLock();
}, "线程A").start();
// 确保A先开始
try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
test1.lock();
test1.business();
test1.unLock();
}, "线程B").start();
}
AtomicReference<Thread> atomicReference = new AtomicReference<>(); // 初始值为null,相当于内存值为null
public void lock(){
System.out.println(Thread.currentThread().getName() + "开始加锁");
Thread thread = Thread.currentThread();
while (!atomicReference.compareAndSet(null, thread)){ // 预期值为null,如果与内存值相等,则替换内存值为thread
System.out.println(Thread.currentThread().getName() +"自旋中");
}
System.out.println(Thread.currentThread().getName() + "加锁成功");
}
public void unLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "解锁");
atomicReference.compareAndSet(thread, null); // 预期值为thread,如果与内存值相等,则替换内存值为null
}
public void business(){
System.out.println(Thread.currentThread().getName() + "开始执行业务");
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "执行完成");
}
}
/*********************************控制台打印结果*********************************/
线程A开始加锁
线程A加锁成功
线程A开始执行业务
线程B开始加锁
线程B自旋中
线程B自旋中
线程B自旋中
......
线程B自旋中
线程A执行完成
线程A解锁
线程B自旋中
线程B加锁成功
线程B开始执行业务
线程B执行完成
线程B解锁