AbstractQueuedSynchronizer
Abstract 抽象
Queue 队列
Synchronize 同步
它是JAVA中多种锁实现的父类,其中有很多地方使用到了CAS操作以提升并发的效率。
CAS
CAS,全称Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制。乐观锁。
CPU原语的执行必须是连续的,在执行过程中不允许被中断。也就是CAS中间不能被打断。
是由若干条指令组成的,用于完成一定功能的一个过程,具有不可分割性,
如 Intel 处理器,比较并交换通过指令的 cmpxchg 系列实现。
CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,
只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,
而是被告知这次竞争中失败,并可以再次尝试。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
JDK1.8 中的CAS
//var1为CAS操作的对象,offset为var1某个属性的地址偏移值,expected为期望值,var2为要设置的值,利用JNI来完成CPU指令的操作
public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2);
public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2);
public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);
CAS缺点
1、ABA问题。当第一个线程执行CAS操作,尚未修改为新值之前,内存中的值已经被其他线程连续修改了两次,
使得变量值经历 A -> B -> A的过程。比如说一个线程one从内存位置V中取出A,
这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,
然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,
然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。这需要对变化的元素进行原子操作。
解决方案:添加版本号作为标识,每次修改变量值时,对应增加版本号;
做CAS操作前需要校验版本号。JDK1.5之后,新增AtomicStampedReference类来处理这种情况。
2、循环时间长开销大。如果有很多个线程并发,CAS自旋可能会长时间不成功,会增大CPU的执行开销。
3、只能对一个变量进原子操作。JDK1.5之后,新增AtomicReference类来处理这种情况,
可以将多个变量放到一个对象中。
Atomic类
实现i++等操作的一个类,保证原子操作。效率比synchronized高。
Atomic开头的几个类,都是原子操作,所以是线程安全的。
采用的原理就是CAS原理实现的。
LongAdder类
实现i++等操作的一个类,保证原子操作。在线程较多的情况下,效率优于Atomic类。
采用分段锁技术,将多个线程分开锁定,分成多块锁,每块锁都进行++操作(CAS),最后将结果加到一起。