WatchDogs

Knowledge of Backend Development

0%

AQS

AQS全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLock、CountDownLatch等。

AQS是一个抽象类,主要是通过继承的方式来使用,它本身没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。

AQS 的内部实现

AQS的实现依赖内部的同步队列,也就是FIFO的双向队列,如果当前线程竞争锁失败,那么AQS会把当前线程以及等待状态信息构造成一个Node加入到同步队列中,同时再阻塞该线程。当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)。

对于 AQS 来说,线程同步的关键是对状态值 state 进行操作.根据 state 是否属于一个线程,操作 state 方式分为独占方式和共享方式。在独占方式下获取和释放资源使用的方法为 void acquire(int arg); void acquirelnterruptibly(int arg); boolean release(int arg);

在共享方式下获取和释放资源的方法为: void acquireShared(int arg); void acquireSharedinterruptibly(int arg); boolean releaseShared(int arg);

###独占方式获取资源

当一个线程调用 acquire(int arg); 获取独占资源时,会首先使用trgAcquire方法尝试获取资源, 具体是设置状态变量 state 的值,成功则直接返回,失败则将当前线程封装为类型为 Node.EXCLUSIVE 的 Node 节点后插入到 AQS 队列的尾部,并调用LockSupport.park(this) 方法挂起自己

1
2
3
4
5
public final void acquire(int arg) { 
if (!tryAcquire(arg) ) &&
acquireQueued(addWaiter Node.EXCLUSIVE), arg))
selfInterrupt();
}

当一个线程调用 release(int arg)方法时尝试使用tryRelease 操作释放资源,这里是设置状态变量 state 的值,然后调用LockSupport.unpark(thread)方法激活 AQS 队列里面被阻塞的一个线程(thread) 被激活的线程则使用 tryAcquire 尝试,看当前状态变量 state 的值是否能满足自己的需要,满足则该线程被激活,然后继续向下运行,否则还是会被放进AQS 队列并被挂起。

1
2
3
4
5
6
7
8
9
public final boolean release(int arg) { 
if (tryRelease (arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor (h);
return true;
}
return false;
}

入队操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}