剑指Offer(锁)——CAS
CAS是一种高效实现线程安全性的方法:
- 支持原子性的更新操作,适用于计数器,序列发生器等场景;
- 属于乐观锁机制,号称lock-free(无锁),但是实际上还是存在底层锁的;
- CAS操作失败时候由开发者决定是继续尝试还是执行别的操作。
CAS操作思想:
包含三个操作数——内存位置(V)、预期原值(A)、新值(B)
比较时候由内存位置和预期原值去进行对比,如果是相同的就会将内存位置的值更新为新值否则处理器不做任何操作,内存位置的值指的是主内存的值。
举个简单的例子:当一个CAS操作想要修改共享变量的值要完成这个操作需要先取出共享变量的值给A,然后基于A的基础去进行计算得到新值B,当执行完毕之后需要更新共享变量的时候可以调用CAS方法去更新变量的值。
将其编译然后用javap看一下字节码的操作指令:
可以看出add方法的value++操作分成了两个部分,getfield操作将value值加载进当前线程的工作内存中然后执行iadd操作将其加一,最后执行putfield将value值写入主内存中,使用了volatile可以保证线程之间的可见性,但是不能保证原子性,在多线程下是不安全的那么该如何解决呢???
解决方法有两种:
- 使用synchronized,加上互斥锁
- 使用Atomic原子操作
总结:
- CAS在大多数情况下对于开发者来说是透明的因为不需要操作指令重排;
- JUC下的atomic包提供了常用的原子性数据类型以及引用、数组等相关原子类型和更新操作工具,是很多线程安全程序的首选;
- Unsafe虽然有CAS操作但是因为能够任意操作内存地址读写存在隐患;
- JDK9中,可以使用Variable Handle API替换Unsafe。
CAS操作也是存在缺点的:
- 如果循环时间过长,开销就会很大;
- 只能保证一个共享变量的原子操作;
- ABA问题(解决方法:使用AtomicStampedReference基于变量版本控制检查是否发生值的替换)。