一、概述
1.1 简介
全称是 AbstractQueuedSynchronizer,它是一个抽象的父类,是相关的同步器工具的框架,即其他的同步器工具都是基于它的,都是它的子类。并且它实现的锁都是阻塞式的锁。和 synchronized 类似,都是阻塞似的锁。
1.2 特点
AbstractQueuedSynchronizer 类里面有个 state 属性表示资源的状态,资源的状态分为独占模式和共享模式。独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源。子类需要定义如何维护这个状态,控制如何获取锁和释放锁。
比如说可以调用 getState() 方法获取 state 的状态。调用 setState() 方法设置 state 的状态,还可以通过 compareAndSetState() 这种 cas 机制设置 state 状态。
AQS 中还提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList。
AQS 中还提供了条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet。
1.3 使用方式
那我们在项目中应该如何使用 AQS 呢?需要通过子类来继承 AbstractQueuedSynchronizer 父类,并实现下面的五个方法:
// 尝试获取锁
tryAcquire()
// 尝试释放锁
tryRelease()
//
tryAcquireShared()
//
tryReleaseShared()
// 是否持有独占锁
isHeldExclusively()
二、使用 AQS 实现不可重入锁
我们要想自己实现一个锁,首先就需要创建一个类去实现 Lock 接口,然后借助 AQS 去实现里面的具体方法,比如说加锁、解锁、设置条件变量等等。接下来详细的展示下创建的过程。
2.1 创建不可重入锁类
首先,创建一个不可重入锁的类 MyLock 并实现 Lock 接口,代码如下所示:
class MyLock implements Lock{
// 加锁,不成功则进入等待队列
@Override
public void lock() {
}
// 可打断的加锁
@Override
public void lockInterruptibly() throws InterruptedException {
}
// 尝试加锁(一次)
@Override
public boolean tryLock() {
return false;
}
// 尝试加锁,带超时时间
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
// 解锁
@Override
public void unlock() {
}
// 创建条件变量
@Override
public Condition newCondition() {
return null;
}
}
2.2 创建 AQS 实现类
在 MyLock 类的内部创建一个 AQS 的实现类 MySync 即可,我们这里只实现一个独占锁,只需要重写下面的三个方法即可,如下代码:
class MyLock implements Lock{
class MySync extends AbstractQueuedSynchronizer{
// 尝试获取锁
@Override
protected boolean tryAcquire(int arg) {
}
// 尝试释放锁
@Override
protected boolean tryRelease(int arg) {
}
// 是否持有独占锁
@Override
protected boolean isHeldExclusively() {
}
// 返回一个条件变量
public Condition newCondition(){
return new ConditionObject();
}
}
// 加锁,不成功则进入等待队列
@Override
public void lock() {
}
// 可打断的加锁
@Override
public void lockInterruptibly() throws InterruptedException {
}
// 尝试加锁(一次)
@Override
public boolean tryLock() {
return false;
}
// 尝试加锁,带超时时间
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
// 解锁
@Override
public void unlock() {
}
// 创建条件变量
@Override
public Condition newCondition() {
return null;
}
}
2.3 实现 AQS 相关方法
接下来我们实现 MySync 内部类里面的方法,其实大部分方法的实现都是借助了 AQS 类里面提供的现成的方法,代码如下:
class MySync extends AbstractQueuedSynchronizer{
// 尝试获取锁
@Override
protected boolean tryAcquire(int arg) {
// 这个 arg 参数暂时没有用到,他是给重入锁计数用的
// state 的初始值为0,我们认为 0 就是未加锁,1就是加锁了
// 使用 cas 的方式保证原子性
if(compareAndSetState(0,1)){
// 设置 owner 为当前线程,和 synchronized 类似
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放锁
@Override
protected boolean tryRelease(int arg) {
// 这个方法不用 cas,因为没有人竞争
// 设置 owner 为 null
setExclusiveOwnerThread(null);
// 放在后面是为了防止指令重排
setState(0);
return true;
}
// 是否持有独占锁
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 返回一个条件变量
public Condition newCondition(){
return new ConditionObject();
}
}
2.4 实现不可重入锁相关方法
接下来我们就来实现下 MyLock 类里面定义的方法,代码如下:
class MyLock implements Lock{
class MySync extends AbstractQueuedSynchronizer{
// 尝试获取锁
@Override
protected boolean tryAcquire(int arg) {
// 这个 arg 参数暂时没有用到,他是给重入锁计数用的
// state 的初始值为0,我们认为 0 就是未加锁,1就是加锁了
// 使用 cas 的方式保证原子性
if(compareAndSetState(0,1)){
// 设置 owner 为当前线程,和 synchronized 类似
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放锁
@Override
protected boolean tryRelease(int arg) {
// 这个方法不用 cas,因为没有人竞争
// 设置 owner 为 null
setExclusiveOwnerThread(null);
// 放在后面是为了防止指令重排
setState(0);
return true;
}
// 是否持有独占锁
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 返回一个条件变量
public Condition newCondition(){
return new ConditionObject();
}
}
private MySync sync = new MySync();
// 加锁,不成功则进入等待队列
@Override
public void lock() {
sync.acquire(1);
}
// 可打断的加锁
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// 尝试加锁(一次)
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
// 尝试加锁,带超时时间
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
// 解锁
@Override
public void unlock() {
sync.release(1);
}
// 创建条件变量
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
2.5 测试
创建两个线程,去争抢同一把锁,观察结果,看线程 t2 是否等待线程 t1 释放锁之后才可以运行,如下代码:
@Slf4j(topic = "c.test")
public class TestAqs {
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(() ->{
try{
lock.lock();
log.debug("locking....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}finally {
log.debug("unlocking....");
lock.unlock();
}
},"t1").start();
new Thread(() ->{
try{
lock.lock();
log.debug("locking....");
}finally {
log.debug("unlocking....");
lock.unlock();
}
},"t2").start();
}
}
输出结果如下,t2 等待 t1 结束之后才开始运行的,没啥问题。