Java中的AQS(转载)

原文链接(https://www.toutiao.com/i6631521306548371981/?group_id=6631521306548371981)

AQS AbstractQueuedSynchronizer,抽象队列同步器
首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁。
ReentrantLock(可重入锁),其使用例子是

ReentrantLock lock = new ReentrantLock();
lock.lock();  //加锁
//业务逻辑代码
lock.unLock(); //解锁

其实ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。
在这里插入图片描述

AQS中有一个核心变量state, 是int类型的,代表了加锁的状态。初始状态下,这个sttede 值是0。另外AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始状态下,这个变量是null。
在这里插入图片描述
接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。
如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。其实看到这儿,大家应该对所谓的AQS有感觉了。说白了,就是并发包里的一个核心组件,里面有state变量、加锁线程变量等核心的东西,维护了加锁状态。
ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。
我们来看看锁的互斥是如何实现的?线程2跑过来一下看到,哎呀!state的值不是0啊?所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!

接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了

在这里插入图片描述

所以大家可以看到,AQS是如此的核心!AQS内部还有一个等待队列,专门放那些加锁失败的线程!
线程1在执行完自己的业务逻辑代码之后,就会释放锁!他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!

接下来,会从等待队列的队头唤醒线程2重新尝试加锁。

好!线程2现在就重新尝试加锁,这时还是用CAS操作将state从0变为1,此时就会成功,成功之后代表加锁成功,就会将state设置为1。

此外,还要把“加锁线程”设置为线程2自己,同时线程2自己就从等待队列中出队了。

最后再来一张图,大家来看看这个过程。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zhou_blog/article/details/89509354