上一篇博客主要讲了AQS的结构。
而这片博客主要针对于同步状态的获取与释放。在此之前,先看一下AQS提供的模板方法
AQS提供的模板方法基本分为3类:独占式获取与释放同步状态,共享式获取与释放同步状态和查询同步队列中的等待线程情况。
一、独占式同步状态获取与释放
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这个方法主要完成了同步状态获取、节点的构造、加入同步队列以及在同步队列中自旋的相关操作。
1、tryAcquire:是自定义AQS实现的。该方法保证线程安全的获取同步状态,获取成功返回true,获取失败则返回false
2、addWaiter:如果tryAcquire返回false,也就是获取同步状态失败,这时候则构造同步节点(独占式)EXCLUSIVE,调用该方法将节点加入到同步队列尾部
3、acquireQueued:该方法是的同步队列中的节点以自旋方式获取同步状态。如果获取不到则阻塞节点中的线程,而被阻塞的线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。
private Node addWaiter(Node mode) {
//传入当前线程构造同步节点
Node node = new Node(Thread.currentThread(), mode);
// 快速尝试在尾部添加
Node pred = tail;
if (pred != null) {
node.prev = pred;
//使用CAS操作确保节点能够被线程安全添加到队列尾部
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
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;
}
}
}
}
从上述代码可以看出,通过使用compareAndSetTail方法来确保节点能够被线程安全添加,在enq方法中,AQS通过死循环来保证节点的正确添加,注意只有通过CAS将节点设置为尾节点之后,当前线程才能从该方法返回,否则,当前线程不断地尝试设置。
在节点进入同步队列之后,就进入了一个自旋的过程,每个节点(或者说每个线程)都在自省的观察,当条件满足,获取到了同步状态,就可以从这个自旋过程中退出,否则依旧留在这个自旋过程中(并会阻塞节点中的线程)。