从ReentrantLock加锁解锁角度分析AQS
Posted xdu-lakers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从ReentrantLock加锁解锁角度分析AQS相关的知识,希望对你有一定的参考价值。
本文用于记录在学习AQS时,以ReentrantLock为切入点,深入源码分析ReentrantLock的加锁和解锁过程。
同步器AQS的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态(通常锁或者同步组件内部会实现一个Sync类(该类是一个静态内部类),然后让Sync类去继承AQS类,通过继承AQS队列同步器并实现它的抽象方法来管理同步状态),对同步状态进行更改需要使用同步器提供的3个方法 getState
、setState
和 compareAndSetState
,它们保证状态改变是安全的。
下图是ReentrantLock中所有的内部类和方法:
3个内部类之间的关系如下图所示:
以ReentrantLock为例,从源码分析如何进行加锁和解锁。
ReentrantLock lc = new ReentrantLock();
lc.lock();
lc.unlock();
(1)ReentrantLock构造函数(为了方便分析,我们以非公平队列为例子)
(2)lc.lock()
ReentrantLock类中的lock()
方法调用sync实例中的lock()方法,因为我们是以非公平队列为例子,所以此时的sync实例的类型为NonfairSync。即调用NonfairSync.lock()
紧接着进入209行对应的acquire(1)方法:该方法位于AQS框架中,且被final修饰,即不能被子类重写。
紧接着进入tryAcquire(int arg)方法
我们发现AQS中的tryAcquire(int arg)方法中没有实现体,说明tryAcquire(int arg)方法需要AQS子类实现,根据上面的继承图可知,Sync以及NonfairSync都是AQS的子类,由下图可知,tryAcquire(int arg)方法的实现位于NonfairSync内部类中
进入NonfairSync内部类中的ryAcquire(int arg)方法可以发现其调用了Sync静态内部类中的nonfairTryAcquire方法。
至此成功获取了锁。
(1) 通过ReentrantLock的加锁方法Lock进行加锁操作。
(2) 会调用到内部类Sync的Lock方法,根据ReentrantLock初始化选择的公平锁和非公平锁,执行相关内部类的Lock方法,本质上都会执行AQS的acquire方法。
(3) AQS的Acquire方法会执行tryAcquire方法,但是由于tryAcquire需要自定义同步器实现,因此执行了ReentrantLock中的tryAcquire方法,由于ReentrantLock是通过公平锁和非公平锁内部类实现的tryAcquire方法,因此会根据锁类型不同,执行不同的tryAcquire。
(4) tryAcquire是获取锁逻辑,获取失败后,会执行框架AQS的后续逻辑,跟ReentrantLock自定义同步器无关。
(3)lc.unlock()
释放锁的过程和加锁过程类似,不走进源码,直接上文字版流程
(1) 通过ReentrantLock的解锁方法Unlock进行解锁。
(2) Unlock会调用内部类Sync的release方法,该方法继承于AQS。
(3) release中会调用tryRelease方法,tryRelease需要自定义同步器实现,tryRelease只在ReentrantLock中的Sync实现,因此可以看出,释放锁的过程,并不区分是否为公平锁。
(4) 释放成功后,所有处理由AQS框架完成,与自定义同步器无关。
经过上述的学习,可以归纳总结出如果要自定义一个同步组件,可以按照如下步骤
- 第一步:内部写一个Sync类继承
AbstractQueuedSynchronizer
接口。 - 第二步:根据是否独占来重写队列同步器中的方法,如果需要独占则实现
tryAcquire()/tryRelease()
等方法,如果不需要独占,则实现tryAcquireShared(int acquires)和tryReleaseShared(int releases)
等方法。 - 第三步:初始化Sync对象,并在锁的获取/释放方法(通常为lock()以及unlock()方法)中调用AQS的
acquire()/release()
或者acquireShared()/releaseShared()
方法。
以上是关于从ReentrantLock加锁解锁角度分析AQS的主要内容,如果未能解决你的问题,请参考以下文章