AQS入门
本文详细介绍AQS相关的知识
概述
AQS全称:Abstract Quened Synchronizer
思路
1.学习AQS的主要目的是了解其原理
2.提供自我技术水平
3.应对面试
4.先了解其场景,再学习如何使用,再掌握其原理
为什么要学习AQS
我们发现ReentrantLock和Semaphore有个共同的特点,即为闸门,都有协作,提取其工具类变成了AQS
同步类和AQS的关系
Semaphore与AQS关系如下图所示:

CountDownLatch为AQS关系如下图所示:

ReentrantLock与AQS关系如下图:

重要性
我们查看下那些类都使用了AQS,见下截图。

AQS核心部分
1.state:实现类不同则含义不同,在Semaphore中代表剩余的许可证的数量;在CountDownLatch代表还需要倒数的数量
相关的方法如下:

2.控制线程配合和抢锁的FIFO队列
此队列用来存放等待线程的队列,双向的链表
3.获取或者释放的重要方法
不同的实现类对应的获取和实现方法都不相同。
AQS用法
我们从CountDownLath、Semaphore、ReentrantLock中分析AQS的用法
CountDownLath中AQS用法
我们从构造、计数、countDown和await进行分析,首先我们看下构造:构造时,我们需要复制sycn,我们将count设置成了state


我们在分析类的继承关系:
接下来我们查看下await方法,如下所示:



private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
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 true;
}
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
Semaphore中AQS用法
我们重点分析下信号量中是如何获取的,即require方法的实现。

final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
上述代码是非公平时,获取信号量的方法,从代码中我可以看到使用了CAS的自旋操作,首先我们获取目前系统中剩余的信号量即为availabe;减去需要申请的信号量,
remaining代表剩余的信号量,当剩余信号量为负数时,说明此时申请失败,继续下一次的循环,当剩余信号量remaining大于等于0时,说明可以申请了,
继续执行CAS
操作修改state值,若修改成功,则获取成功;若修改失败,则代表被其他线程抢走了,继续执行循环直到申请到为止。
ReentrantLock中AQS用法
我们熟知ReenTrantLock是一把可重入锁,本实例我们从非公平的情况下进行分析,首先我看ReenTrantLock内部类的结构或者相关的方法。如下图所示:

接下来我们分析非公平的加锁的实现。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
从代码结构中可以看到:首先我们执行CAS的加锁操作,将state=0 修改成state=1;
若修改成功,则标记当前线程为锁的持有者;
若修改失败,则我们基于执行acquire方法,
接下来我们分析acquire方法的实现:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
下面代码为tryAcquire的实现。
我们可以看到:首先我获取state的值,如果state=0;说明锁没有被占用,然后CAS操作,设置当前线程持有锁;
如果state不等于0,判断是不是持有者,如果是,则进行重入操作,state+1;
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
下面代码为释放锁的代码实现,如下图:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
我们发现释放锁的时候,需要判断当前线程是不是持有锁,否则会抛出异常;若c=0,则直接释放;否则重入次数减去1

浙公网安备 33010602011771号