AQS概述
结合下面的类图,可以知道AQS可以理解为同步器Sync的一部分,线程加锁,释放锁的逻辑均是在这里面进行的,是队列中非常重要的一个类。
实现原理
由上图我们可以清楚的看出,ReentrantLock,WriteLock,ReadLock中均有一个同步器Sync,当然不仅仅是这3个中有同步器,CountDownLatch,Semaphore其中也都有同步器Sync。
同时,由图中可以看出,同步器Sync的父类AbstractQueuedSynchronizer是一个抽象类,Sync中很多的方法都是调用父类AQS中的方法进行实现的。
所以接下来了,借助,Sync的使用来看下AQS到底是怎样进行工作的。
1、AQS属性,构造器
1.1 属性
// 表示队列的头节点
private transient volatile AbstractQueuedSynchronizer.Node head;
// 表示队列的尾部节点
private transient volatile AbstractQueuedSynchronizer.Node tail;
// 表示当前先线程是否获取到了锁。0:未获取锁 非0 :获取到了锁
private volatile int state;
static final long spinForTimeoutThreshold = 1000L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
1.2 构造器
protected AbstractQueuedSynchronizer() {
}
AQS只有一个无参的构造器。
1.3 内部类
static final class Node {
// 共享模式
static final AbstractQueuedSynchronizer.Node SHARED = new AbstractQueuedSynchronizer.Node();
// 独占模式
static final AbstractQueuedSynchronizer.Node EXCLUSIVE = null;
// waitStatus 被中断
static final int CANCELLED = 1;
// waitStatus 该线程被唤醒
static final int SIGNAL = -1;
// waitStatus 该线程处于wiating状态
static final int CONDITION = -2;
// waitStatus 传播
static final int PROPAGATE = -3;
volatile int waitStatus;
// 当前节点的前一个节点
volatile AbstractQueuedSynchronizer.Node prev;
// 当前节点的下一个节点
volatile AbstractQueuedSynchronizer.Node next;
// 当前节点对应的线程
volatile Thread thread;
AbstractQueuedSynchronizer.Node nextWaiter;
final boolean isShared() {
return this.nextWaiter == SHARED;
}
final AbstractQueuedSynchronizer.Node predecessor() throws NullPointerException {
AbstractQueuedSynchronizer.Node var1 = this.prev;
if (var1 == null) {
throw new NullPointerException();
} else {
return var1;
}
}
Node() {
}
Node(Thread var1, AbstractQueuedSynchronizer.Node var2) {
this.nextWaiter = var2;
this.thread = var1;
}
Node(Thread var1, int var2) {
this.waitStatus = var2;
this.thread = var1;
}
}
#### 2、同步器 由上图,我们可以看出,同步器Sync的实现有公平锁和非公平锁2种。 - 公平锁,指的是多个线程在处理任务时,其中一个线程获取到了锁,其他的线程就会按照顺序进入队列进行等待。当锁被释放之后,位于队列的第一个等待线程就会被取出去执行业务。
- 非公平锁,指的是多个线程对于锁的获取是抢占式的,通过CAS算法随机的进行对锁的抢占,持有。有可能同一个线程会多次抢到同一个锁,所以是不公平的。
2、公平锁
2.1 lock-加锁
下面就以公平锁的源码实现来理解AQS
final void lock() {
acquire(1);
}
当调用 FairSync.lock的方法时,其实是调用了AQS.acquire(int arg)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- tryAcquire(arg):这个方法在AQS中只是抛出了一个 new UnsupportedOperationException()的异常。具体的实现是由FairSync实现的。
所以,我们接着往下看FairSync.tryAcquire(),是否加锁成功。
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取当前线程的状态,0 代表没有获得锁,非0 代表获得了锁
int c = getState();
if (c == 0) {
hasQueuedPredecessors() 队列中,当前节点的前节点是否存在。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 成功cas,那么代表当前线程获得该变量的所有权,也就是说成功获得锁
setExclusiveOwnerThread(current);
// 设置当前线程为 独占性变量所有者线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 判断当前线程的引用是否与独占性所有者线程的引用一致
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
// setState 加锁成功
return true;
}
return false;
}
}
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter(Node.EXCLUSIVE) 没有获取到锁,则将当前线程加入至队列。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
acquireQueued()方法
由于进入阻塞状态的操作会降低执行效率,所以,AQS会尽力避免试图获取独占性变量的线程进入阻塞状态。所以,当线程加入等待队列之后,acquireQueued会执行一个for循环,每次都判断当前节点是否应该获得这个变量(在队首了)。如果不应该获取或在再次尝试获取失败,那么就调用shouldParkAfterFailedAcquire判断是否应该进入阻塞状态。如果当前节点之前的节点已经进入阻塞状态了,那么就可以判定当前节点不可能获取到锁,为了防止CPU不停的执行for循环,消耗CPU资源,调用parkAndCheckInterrupt函数来进入阻塞状态。
shouldParkAfterFailedAcquire。
2.2 unlock-释放锁
public void unlock() {
sync.release(1);
}
调用sync.release(1),其实质还是调用了AQS.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()的处理逻辑如下
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;
}
将当前资源进行锁释放。