什么是AQS
AQS(Abustact Queued Synchronizer 抽象队列化同步器)是用来为Java的并发同步组件提供统一的底层支持,例如 ReentrantLock, CountdowLatch 都是基于AQS实现的。AQS采用模板方法设计模式,我们可以通过简单的继承实现模板方法,来自定义我们自己的锁。
AQS的常用方法
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
独占式获取(独占锁)
accquire
acquireInterruptibly
tryAcquireNanos
共享式获取(共享锁)
acquireShared
acquireSharedInterruptibly
tryAcquireSharedNanos
独占式释放锁
release
共享式释放锁
releaseShared
需要子类覆盖的流程方法
独占式获取 tryAcquire
独占式释放 tryRelease
共享式获取 tryAcquireShared
共享式释放 tryReleaseShared
这个同步器是否处于独占模式 isHeldExclusively
同步状态state:
getState:获取当前的同步状态
setState:设置当前同步状态
compareAndSetState 使用CAS设置状态,保证状态设置的原子性
AQS中的数据结构
AQS有2个重要组成部分:
- state 同步状态:int 类型
- 一个同步队列:当一个线程尝试获取锁时,如果已经被占用,此线程就作为一个节点添加到队尾,对头是成功获取锁的节点,对头释放锁后,会唤醒后面的节点,并出队。
自定义锁
实现Lock接口,方法通过AQS实现;在内部定义一个AQS,复写模板方法。其中state = 1表示获取到了锁, state = 0 表示这个锁没有线程拿到,不可重入。如果要做成可重入的,只需每次拿锁 stats + 1, 释放 stats - 1即可。
当前这个是独占锁,而共享锁的实现类似;只需初始化定义stats = n,然后每次拿到锁之后 stats - 1, 当stats < 0 后等待。
注意所有拿锁的stats操作必须遵循CAS操作,即满足原子性。
public class SelfLock implements Lock {
// state = 1表示获取到了锁, state = 0 表示这个锁没有线程拿到,不可重入
private static class Sync extends AbstractQueuedSynchronizer {
// 判别当前锁是否占用
protected boolean isHeldExclusively() {
return getState() == 1;
}
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0, 1)) {
// 设置当前线程拿到锁
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
if(getState() == 0) {
throw new UnsupportedOperationException();
}
// 由于只有一个线程能到持有锁,故这里不需要CAS操作
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
private final Sync sync = new Sync();
@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();
}
}