一、为什么需要AQS
仔细观察锁和协作类,就会发现有一个共同点:闸门。
既然这些协作类,它们有很多工作都是类似的,所以如果提取出一个工具类,那么就可以直接用。对于ReentrantLock和Semaphore的实现而言,就可以屏蔽很多细节,只关注自己的业务逻辑就可以了。
1.如果没有AQS
就需要每个工具类自己实现:
- 同步状态的原子性管理
- 线程的阻塞与解除阻塞
- 队列的管理
2.Semaphore和AQS的关系
Semaphore内部有一个Sync类,其继承了AQS。
同样的,CountDownLatch中也有一个Sync内部类。
3.AQS的作用
AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS以后,更多的协作工具类都可以很方便的被写出来。
二、内部原理解析
1.核心三大部分
(1)state状态
state具体的含义根据具体实现类的不同而不同。比如CountDownLatch中表示“还需倒数数量”,Semaphore中表示“剩余许可证数量”。
state由volatile修饰,所有修改state的方法都需要保证线程安全。
(2)控制线程抢锁和配合的FIFO队列
用来存放等待的线程。
(3)期望协作工具类实现的 获取/释放 等重要方法
这个方法需要协作类自己实现,且含义各不相同。
- 获取操作会依赖state变量,经常会阻塞;
- 释放操作不会阻塞;
2.源码解析
(1)AQS用法
- 写一个类,想好协作的逻辑,实现获取/释放方法
- 内部写一个Sync类继承AbstractQueuedSynchronizer
- 独占则重写tryAcquire/tryRelease。共享则重写tryAcquireShared(int acquires)和tryReleaseShared(int releases)等方法。在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法。
(2)CountDownLatch源码分析
构造方法
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
构造方法中根据传入的count构造Sync实例。
Sync(int count) {
setState(count);
}
getCount()
public long getCount() {
return sync.getCount();
}
//sync中的
int getCount() {
return getState();
}
await()
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
如果tryAcquireShared(arg) < 0
,也就是没有获取到锁,那么就要入队并且使其阻塞。如果>0说明正常获取到锁,可以继续执行。
CountDownLatch中是这样实现tryAcquireShared的:
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
countDown()
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared同样是在CountDownLatch中实现的:
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
可以看到,在release的时候是不用阻塞线程的,它是利用CAS来实现state值的-1的。
如果此时state的值为0,那么就返回true。当返回true时,执行doReleaseShared();
,会唤醒所有线程。
(3)Semaphore源码分析
aquire
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
可以看到,和CountDownLatch的await()方法是相似的,区别在于Semaphore这里可以获取指定数量。
tryAcquireShared
有公平、非公平两种实现:
nonfairTryAcquireShared(int acquires)
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
如果小于0,说明许可证不够用,那么在调用它的acquireSharedInterruptibly
中就会把这个线程入队并阻塞。
大于0意味着成功拿到许可证。
tryAcquireShared
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
和非公平的相比,有了一个新的判断条件,这个判断条件就是判断是否是队首。hasQueuedPredecessors()为true意味着不是队首,直接返回-1。为false说明是队首,则可以尝试获取许可证。
(4)ReentrantLock源码分析
非公平锁的lock()
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
如果锁没有持有者,则设置此线程为持有者;否则aquire()。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先将传入的参数,传入到tryAcquire方法中,这个方法在非公平锁中的实现为:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
其逻辑为首先判断锁是否有持有者,没有则将当前线程作为持有者;
有的话判断当前线程是不是就是持有者,是的话重入acquires次;
都不是返回false。
unLock()
public void unlock() {
sync.release(1);
}
调用release()方法:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
首先会执行tryRelease(arg)进行尝试释放锁:
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;
}
如果当前线程不是锁的持有者,返回false;
如果state减去releases后值为0,说明这把锁已经没有人持有了,此时将锁的持有者设为null,state设置为0,返回true。
此时在release中倘若返回结果为true,则唤醒所有被阻塞的线程。