AQS 简介
AbstractQueuedSynchronizer抽象队列同步器
,简称为AQS
,可用于构建阻塞锁或者其他相关同步器的基础框,是Java并发包的基础工具类。通过AQS这个框架可以对同步状态原子性管理、线程的阻塞和解除阻塞、队列的管理进行统一管理。
AQS是抽象类,并不能直接实例化,当需要使用AQS的时候需要继承AQS抽象类并且重写指定的方法,这些重写方法包括线程获取资源和释放资源的方式(如ReentractLock通过分别重写线程获取和释放资源的方式实现了公平锁
和非公平锁
),同时子类还需要负责共享变量state
的维护,如当state为0时表示该锁没有被占,大于0时候代表该锁被一个或多个线程占领(重入锁),而队列的维护(获取资源失败入队、线程唤醒、线程的状态等)不需要我们考虑,AQS已经帮我们实现好了。AQS的这种设计模式采用的正是模板方法模式。
AQS的内部结构
AQS内部数据结构为一个双向链表
和一个单向链表
,双链表为同步队列
,队列中的每个节点对应一个Node内部类,AQS通过控制链表的节点而达到阻塞、同步的目的,单链表为条件队列
,可以把同步队列和条件队列理解成储存等待状态的线程的队列,但是条件队列中的线程并不能直接去获取资源,而要先从条件队列转到同步队列中排队获取,同步队列的唤醒结果是线程去尝试获取锁,而条件队列的唤醒
结果是把线程从条件队列移到同步队列
中,一个线程要么是在同步队列中,要么是在条件队列中,不可能同时存在这两个队列里面。
从下面例子就可以看到对两个队列的操作, 从而达到对线程的交互.
ReentrantLock和Condition组件搭配使用:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class LockExample6 {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
new Thread(() -> {
try {
reentrantLock.lock(); // 此时被加入到了AQS等待序列中
log.info("wait signal"); // 1
condition.await(); // 又被AQS移除了(await操作释放了锁) 进入了Condition的等待队列中
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get signal"); // 4 获得信号
reentrantLock.unlock();
}).start();
new Thread(() -> {
reentrantLock.lock(); // 获得锁 进入AQS等待序列中
log.info("get lock"); // 2
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signalAll(); // 发送信号唤醒所有等待的线程 将Condition等待队列中的线程1节点的 取出并加入到AQS等待队列中
log.info("send signal ~ "); // 3
reentrantLock.unlock();
}).start();
}
}