AQS笔记二 ---- 自定义锁
AQS(AbstractQueuedSynchronizer) 队列同步器, AQS定义了一套多线程访问共享资源的同步器框架.
AQS 内部依赖的同步队列(一个FIFO双向队列)来完成同步状态的管理, 当前线程获取同步状态失败时, 同步器会将当前线程以及等待状态等信息构成一个节点(Node) 并将其加入到同步队列, 同时阻塞当前线程, 当同步状态释放时, 会将首节点中的线程唤醒, 使其再次尝试获取同步状态.
同步器是实现锁(也可以是任意同步组件)的关键, 在锁的实现中聚合同步器, 利用同步器实现锁的语义, 可以理解二者之间的关系: 锁是面向使用者的, 它定义了使用者与锁交互的接口, 隐藏了实现细节; 同步器是锁的实现者, 它简化了锁的实现方式, 屏蔽了同步状态管理, 线程的排队,等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者所需关注的领域;
同步器的设计是基于模板方法模式, 使用者需要继承同步器并重写指定的方法, 随后将同步器组合在自定义同步组件的实现中, 并调用同步器提供的模板方法;
一、实现自定义同步组件依靠的几个方法
重写同步器指定方法时, 需要使用同步器提供的如下3个方法来访问修改同步状态;
- getState() 获取当前同步状态;
- setSate(int newState) 设置当前同步状态
- compareAndSetState(int expect, int update) 使用CAS设置当前状态, 该方法能保证状态设置的原子性;
1.1 同步器可重写的方法
1.2 同步器提供的模板方法
实现自定义同步组件时, 会遇到调用同步器提供的模板方法, 模板方法如下:
二、 自定义锁
2.1 非重入排他锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class Metex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer{
// 是否处于占用状态
protected boolean isHeldExclusively(){
return getState() == 1;
}
// 尝试获取同步状态
public boolean tryAcquire(int acquires){
if(compareAndSetState(0, 1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 同步状态, 将同步状态置为0
protected boolean tryRelease(int releases){
if(getState() == 0) throw new IllegalArgumentException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 返回一个Condition, 每个condition都包含一个condition队列
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.tryRelease(1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}