1. 什么是AQS?
2. AQS作用
3. AQS的重要性、地位
4. AQS内部原理解析(同步状态、同步队列、获取/释放方法)
5. AQS在CountDownLatch应用
6. AQS在Semaphore应用
7. AQS在ReentrantLock应用
8. AQS实现一个简单的线程协作器
AQS ,AbstractQueuedSynchronizer ,即队列同步器。它是构建锁或者其他同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等),J.U.C 并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。它是 J.U.C 并发包中的核心基础组件
AQS是一个用于构建锁、同步器、协作工具类的工具类(框架),解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO 同步队列,有了AQS以后,更多的协作工具类都可以很方便的被写出来
在基于 AQS 构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计 AQS 时充分考虑了可伸缩性,因此 J.U.C 中,所有基于 AQS 构建的同步器均可以获得这个优势。
AbstractQueuedSynchronizer是Doug Lea写的,从JDK1.5加入的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架
JUC包里面几乎所有的有关锁、多线程并发以及线程同步器等组件的实现都是基于AQS这个框架
AQS最核心的三大部分: state、控制线程抢锁和配合的FIFO队列、期望协作工具类去实现的获取/释放等重要方法
4.1 同步状态
AQS的主要使用方式是继承,子类通过继承同步器,并实现它的抽象方法来管理同步状态
AQS使用一个int
类型的成员变量state来表示同步状态
state > 0
时,表示已经获取了锁state = 0
时,表示释放了锁它提供了三个方法,来对同步状态state
进行操作,并且AQS可以确保对state
的操作是安全的:
#getState()
#setState(int newState)
#compareAndSetState(int expect, int update)
state的具体含义会根据具体实现类的不同而不同,比如在Semaphore
里,它表示"剩余的许可证的数量",而在CountDownLatch
里,它表示"还需要倒数的数量",在ReentrantLock
中,它表示"锁"的占有情况,包括可重入计数
4.2 同步队列
AQS通过内置的FIFO同步队列(双向队列)来完成资源获取线程的排队工作:
4.3 获取/释放方法
获取方法
获取操作会依赖state
变量,经常会阻塞(比如获取不到的时候)
#acquire(int arg)
:独占式获取同步状态。如果当前线程获取同步状态成功,则由该方法返回;否则,将会进入同步队列等待。该方法将会调用可重写的 #tryAcquire(int arg)
方法
#acquireShared(int arg)
:共享式获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态
在Semaphore中,获取就是acquire方法,作用是获取一个许可证
在CountDownLatch中,获取就是await方法,作用是"等待,直到倒数结束"
释放方法
释放操作不会阻塞
#release(int arg)
:独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒
#releaseShared(int arg)
:共享式释放同步状态
在Semaphore中,释放就是release方法,作用是释放一个许可证
在CountDownLatch中,释放就是countDown方法,作用是"倒数1个数"
需要重写tryAcquire和tryRelease等方法
#tryAcquire(int arg)
:独占式获取同步状态,获取同步状态成功后,其他线程需要等待该线程释放同步状态才能获取同步状态。#tryRelease(int arg)
:独占式释放同步状态。#tryAcquireShared(int arg)
:共享式获取同步状态,返回值大于等于 0 ,则表示获取成功;否则,获取失败。#tryReleaseShared(int arg)
:共享式释放同步状态。5.1 构造函数
//构造一个用给定计数初始化的 CountDownLatch
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);
}
protected final void setState(int newState) {
state = newState;
}
5.2 await方法
CountDownLatch 提供 #await()
方法,来使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断
// CountDownLatch.java
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// AQS.java 该方法内部使用 AQS 的 #acquireSharedInterruptibly(int arg) 方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
//在内部类 Sync 中重写了 #tryAcquireShared(int arg)方法
protected int tryAcquireShared(int acquires) {
//getState() 方法,获取同步状态,其值等于计数器的值
// 等于0则表示倒计数为0,放行所有等待的线程,否则,则挂起当前线程
return (getState() == 0) ? 1 : -1;
}
// AQS.java 该方法为一个自旋方法会尝试一直去获取同步状态,挂起当前线程
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5.3 countDown
CountDownLatch 提供 #countDown()
方法,递减锁存器的计数。如果计数到达零,则唤醒所有等待的线程
// CountDownLatch.java
public void countDown() {
sync.releaseShared(1);
}
// 内部调用 AQS 的 #releaseShared(int arg) 方法,来释放共享锁同步状态
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
// 如果释放成功将会释放之前阻塞的线程 unparkSuccessor
doReleaseShared();
return true;
}
return false;
}
// 被CountDownLatch内部类Sync重写了AQS的共享释放方法
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
// 获取锁状态
int c = getState();
// 当state=0时候,说明之前已经释放过了,本次就不需要重复释放了
if (c == 0)
return false;
// 计算新“锁计数器”
int nextc = c-1;
// 用cas更新锁状态(计数器)
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
在Semaphore中,state表示许可证的剩余数量
6.1 acquire方法
//Semaphore.java
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// AQS.java
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
// 阻塞等待,信号量不足时阻塞线程
doAcquireSharedInterruptibly(arg);
}
// Semaphore.java
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
// Semaphore.java
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//获取当前的信号量许可
int available = getState();
// 设置“获得acquires个信号量许可之后,剩余的信号量许可数”
int remaining = available - acquires;
// CAS设置信号量
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
6.2 release方法
// Semaphore.java
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
// AQS.java
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//可唤醒阻塞等待 Semaphore 的许可的线程
doReleaseShared();
return true;
}
return false;
}
// Semaphore.java
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
//信号量的许可数 = 当前信号许可数 + 待释放的信号许可数
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//设置可获取的信号许可数为next
if (compareAndSetState(current, next))
return true;
}
}
lock方法
// ReentrantLock.java
public void lock() {
sync.lock();
}
// ReentrantLock.java
final void lock() {
//#lock() 实现方法,首先基于 AQS state 进行 CAS 操作,将 0 => 1 。
//若成功,则获取锁成功。若失败,执行 AQS 的正常的同步状态获取逻辑
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// AQS.java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// ReentrantLock.java
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// ReentrantLock.java
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//state == 0,表示没有该锁处于空闲状态
if (c == 0) {
//获取锁成功,设置为当前线程所有
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//线程重入
//判断锁持有的线程是否为当前线程
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
#nonfairTryAcquire
方法主要逻辑:首先判断同步状态 state == 0 ?
unlock
// ReentrantLock.java
public void unlock() {
sync.release(1);
}
// AQS.java
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// ReentrantLock.java
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果释放的不是持有锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// state == 0 表示已经释放完全了,其他线程可以获取同步状态了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
public class OneShotLatch {
private final Sync sync = new Sync();
public void signal(){
sync.releaseShared(0);
}
public void await(){
sync.acquireShared(0);
}
private class Sync extends AbstractQueuedSynchronizer {
@Override
protected int tryAcquireShared(int arg) {
// 是否开闸,如果是返回1, 否则返回-1<0进入等待队列
return getState() == 1 ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
// state = 1 表示开闸, state = 0表示关闸
setState(1);
return true;
}
}
public static void main(String[] args) throws InterruptedException {
OneShotLatch oneShotLatch = new OneShotLatch();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "尝试获取latch,获取失败就等待");
oneShotLatch.await();
System.out.println(Thread.currentThread().getName() + "开闸放行,继续运行");
}
}).start();
}
Thread.sleep(4000);
oneShotLatch.signal();
}
}
参考: