本文转载自:石杉的架构笔记
AQS(AbstractQueuedSynchronizer):抽象队列同步器
我们先看一段代码:
public void test(){
ReentrantLock lock = new ReentrantLock();
//加锁
lock.lock();
//业务逻辑代码
//释放锁
lock.unlock();
}
上面代码无非是搞一个Lock对象,然后加锁和释放锁.
Java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类.
比如说ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。
我们来看上面的图。说白了,ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。
现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情呢?
很简单,这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。
另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。
接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS(先比较再设置)操作将state值从0变为1。一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。所以大家看下面的图,就是线程1跑过来加锁的一个过程。
ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。
这个ReentrantLock之所以用Reentrant打头,意思就是他是一个可重入锁。
可重入锁的意思,就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。
其实每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化。
如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?
线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了.
线程1在执行完自己的业务逻辑代码之后,就会释放锁!他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!
接下里线程2进行加锁:
总结:AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的.它包含了state变量,加锁线程,等待队列等并发中的核心组件.