原文链接:AQS的初步了解 – 编程屋
目录
1 什么是AQS
AQS(AbstractQueuedSynchronizer): 是jdk提供的一个同步框架,内部维护着FIFO双向队列,即CLH同步队列。
AQS依赖它来完成同步状态的管理(volatile修饰的state,用于标志是否持有锁)。如果获取同步状态state失败时,会将当前线程和等待信息构建成一个Node节点。将Node放到FIFO队列中,同时阻塞当前线程,当前线程同步状态state释放时,会把FIFO队列中的首节点唤醒,使其获取同步状态state。
/**
* The synchronization state.
*/
private volatile int state;
注意:java并发包下有很多API都是基于AQS来实现加锁和解锁释放锁的功能的,AQS是java并发包的基础类,像ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。
2 ReentrantLock加锁和释放锁的底层原理
给大家画一张图,可以让大家更好的理解AQS和ReentrantLock的关系。
如图所示:ReentrantLock(还是JUC类中一些其他的并发类)中包含了AQS,ReentrantLock的加锁和解锁的功能就是依靠AQS来实现的。
假如此时有一个线程来用ReentrantLock进行lock()加锁。AOS内部有一个核心变量叫做state,并且用volatile修饰,初始值为0,并且AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始状态下,这个变量值null。
1)首先会用CAS操作将state从0变为1
注意:如果之前没有线程加过锁,才会加锁成功。否则会加锁失败,然后失败线程进入队列。
2)加锁线程会记录下当前线程的名字
但是ReentrantLock是一个可重入锁,就是对于同一线程,可以进行多次加锁(必须是同一线程)。
如果线程1可重入加锁几次,会先判断一下当前线程是不是自己,那么state就会进行相应的累加,其它的不变。
但是此时又来了一个线程2怎么办呢?
此时如果来了一个线程2,发现state的状态不是0,线程1目前还没释放锁,state的状态依然是1。那么线程2的CAS失败会将自己放入等待队列。
总结:AQS的功能主要分为两类:独占和共享。它的所有子类中要么实现并使用了它的独占功能的API,要么使用了共享锁的功能。而不会同时使用两套API,即便是最有名的子类ReentrantReadWriteLock也是通过两个内部类读锁和写锁分别实现了两套api来实现的。
以上只是部分内容,为了维护方便,本文已迁移到新地址:AQS的初步了解 – 编程屋