1. AQS 原理
1.1 AQS 简介
AQS 全名 AbstractQueuedSynchronizer,是一个队列同步器,JUC 的锁和同步组件都是基于 AQS 构建的。AQS 的构造次要分两局部:
- state:用了 violate 关键字润饰的 int 类型成员变量,示意同步状态,每次更新都能被所有线程可见;
- head、tail:内置的 Node 类型(Node 蕴含以后线程、期待状态等信息),组成了一个FIFO 的同步队列。
AQS 的次要应用形式是继承,它采纳了模板办法的设计模式。什么是模板办法设计模式?简略来说,就是 一个类将一些不变行为封装成 final 办法(即模板办法),将残余的可变办法(abstract)留给子类实现去拓展。
1.2 AQS 接口
AQS 次要提供了如下三个办法来拜访或批改同步状态 state:
- getState():获取以后同步状态
- setState(int newState):设置以后同步状态
- compareAndSetState(int expect, int update):应用 CAS 设置以后状态,该办法可能保障状态设置的原子性。
1.2.1 AQS 模板办法
上面列举几个最为外围的模板办法:
办法 | 形容 |
---|---|
void acquire(int arg) | 独占式调用 tryAquire()尝试获取同步状态,获取失败则将该线程塞进同步队列队尾 |
void acquireShared(int arg) | 共享式调用 tryAquireShared()获取同步状态,同一时刻能够有多个线程获取到同步状态 |
boolean release(int arg) | 独占式调用 tryRelease()开释同步状态 |
boolean releaseShared(int arg) | 共享式调用 tryReleaseShared()开释同步状态 |
1.2.2 AQS 重写接口
而 AQS 可重写的办法如下:
办法 | 形容 |
---|---|
boolean tryAquire(int arg) | 独占式获取同步状态 |
boolean tryRelease(int arg) | 独占式开释同步状态 |
int tryAquireShared(int arg) | 共享式获取同步状态,与其余接口不同的是该接口返回值是 int,这是为了实现共享的语义,返回大于等于 0 时示意胜利,反之失败 |
boolean tryReleaseShared(int arg) | 共享式开释同步状态 |
能够看到,AQS 利用模板办法次要做了两件事:
- 尝试获取或设置同步状态
- 保护同步队列
而留给开发者的可重写办法更多聚焦于如何 设置、获取同步状态,这种设计思维与双亲委派机制有点类似:loadClass 中实现了双亲委派机制,而用户能够通过重写 findClass 去实现本人加载类的具体逻辑。
2. Lock 和 AQS 的关系
AQS 是实现 Lock 的根底,两者之间的次要区别在于:
- Lock 面向锁的使用者,它聚焦的问题是 使用者如何更好地应用锁解决并发问题,而使用者不须要晓得锁的实现细节就能够实现互斥同步;
- AQS 面向锁的开发者,它关注的两个次要问题是 同步状态治理 以及 保护线程的同步期待队列。
3. 自定义实现独占锁
该节通过自定义实现独占锁 Demo,去更加清晰揭示 AQS 和 Lock
接口的区别。
首先,咱们定义了一个独占锁—Mutex
实现 Lock
接口,并实现该接口的所有办法。而 Lock 接口的办法实现中所做的是都是调用 AQS 的模板办法。
其次,咱们创立了一个外部类—Sync,实现了一个自定义同步器,并重写了其中的所有非模板办法,次要思维是 通过 getState()、setState(int newState)和 compareAndSetState(int expect, int update)三个办法去设置和获取同步状态,具体实现可看代码正文。
至此咱们总结一下其中的一个外围调用链lock --> acquire() --> tryAcquire()
:
lock
:面向用户提供锁性能acquire
:尝试设置同步状态并保护同步队列tryAquire
:设置同步状态的真正逻辑
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @author Lam
* @date 2020/7/27
*/
public class Mutex implements Lock {private final Sync sync = new Sync();
/**
* 同步器实现,重写非模板办法
* 次要思维是应用 CAS 设置 state 的状态
*/
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
// CAS 设置 state
if (compareAndSetState(0, 1)) {
// 设置该线程为胜利获取同步状态的线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
// 设置状态失败
return false;
}
@Override
protected boolean tryRelease(int arg) {
// 若此时同步状态为 0 阐明没有线程设置过同步状态
if (getState()==0) {throw new IllegalMonitorStateException();
}
// 清空胜利获取同步状态的线程信息
setExclusiveOwnerThread(null);
// 革除状态
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {return getState() == 1;
}
/**
* 该办法不是 AQS 的重写办法,只是为了代码格调对立将逻辑都放到 Sync 中
*/
Condition newCondition() {
// 返回一个期待告诉组件
return new ConditionObject();}
}
/**
* Lock 接口的实现
*/
@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();
}
}
4. 总结
本文大部分内容取自 Java 并发编程的艺术,在一些小中央做了删改并加了本人的了解,如果想更齐全深刻理解 AQS 的模板办法和非模板办法有哪些能够查看原书第五章的 1、2 节。