java 并发编程之CAS与AQS

还有一个月就要过春节了,过完春节回来又到了一年一度的换工作的时间了,尤其是程序员只能靠跳槽来使工资保证能达到市场的平均水平,而在面试中CAS和AQS又是面试经常问到的问题,希望明年准备换工作的朋友们面试能够用的上。

CAS:Compare And Swap 即比较替换的意思,在多线程并发中我们可以使用锁来保证线程的安全问题,但是使用锁机制也会导致性能问题,比如使用Synchornized就会引起线程阻塞的问题,CAS就是为了解决这个问题应运而生的。在jdk中大量的API通过CAS来解决多线程中的线程安全问题,我们通过一个原子类来看下CAS的使用。AtomicInteger就是一个使用了CAS的原子类,我们通过源码来看下。

java 并发编程之CAS与AQS_第1张图片

从AtomicInteger的源码可以看到compareAndSet()方法的内部实现其实是由unsafe类下的compareAndSwapInt()方法实现的,这些compareAndSwap开头的方法都是基于CAS来实现的,unsafe提供了硬件级别的原子操作来实现CAS,那么CAS到底是什么呢?

CAS包括三个操作数:内存位置、预期原值、新值。如果内存位置的值与预期原值匹配,那么就把内存位置的值更新为新值,否则的话处理器不做操作。CAS说明了处理器认为内存位置应该包含预期原值,如果包含则将新值放到这个位置,否则返回该位置的值即可。

但是CAS这样也是存在问题的,就是大家熟知的ABA的问题,如果不知道ABA的朋友听我给你慢慢说来,如果有一个线程1对一个值A进行操作计算,在线程1还未操作完成时,线程2把这个值改为了B,然后又有一个线程3又把B改为了A,这时线程1计算完毕,然后就通过CAS来检查A,发现内存的值与原来相等,就直接把计算的值更新掉了。

java 并发编程之CAS与AQS_第2张图片

这就是ABA问题,出现这个问题的原因是原来内存中的A与后来的A没有区别,所以不知道这是被修改过的。后来java又提供了AtomicStampedReference类来解决CAS的ABA问题,解决办法就是给值加上一个版本号来区分是否被修改过。

这就是CAS了,至于在硬件层面是怎么设计的,就暂时不是咱们考虑的了。

那么AQS又是什么呢?

AQS:AbstractQueuedSynchronizer 的缩写,AQS是一个提供了原子式管理同步状态、阻塞功能和线程唤醒功能以及队列的一个同步框架。

AQS的思想就是如果被请求的共享资源空闲的话,就把当前请求的线程设置为有效的线程,并且把共享资源进行锁定,当其它线程来请求的时候,共享资源被占用,就把请求线程放入队列进行等待,如果有效线程任务完成,就按顺序把队列中的线程设置为有效线程。在AQS中state 默认为0,表示没有线程加锁,如果已经加锁则state = 1,有效线程默认为null,如果有加锁线程,则有效线程就是加锁线程,等待队列就是存放等待中的线程的。我们借助ReentrantLock来理解AQS的原理,下图是默认状态。

java 并发编程之CAS与AQS_第3张图片

如果有线程1进行了加锁,那么Thread1 就会使用CAS操作使AQS state = 1,有效线程等于线程1.

java 并发编程之CAS与AQS_第4张图片

如果现在又有一个线程,Thread2来请求共享资源,那么Thread检查到state = 1,说明已经有人加了锁了并且不是自己,通过有效线程可以看到是Thread1占用了锁,那么这时候Thread2就是加锁失败,就会进入到等待队列中进行排队,等到Thread1的逻辑执行完成后,Thread就会使用CAS操作把state置为0,就彻底释放了锁,有效线程置为null, Thread2自己就可以再次尝试加锁了。

java 并发编程之CAS与AQS_第5张图片

看到这里大家应该明白了AQS是什么了吧,AQS就是一个包含了state变量,等待队列等,用来处理并发的基础组件,用来实现各种锁和各种同步组件的。

你可能感兴趣的:(线程)