谈谈你对AQS的理解:
AQS 的全称为 **AbstractQueuedSynchronizer**
,翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks
包下面。AQS 为构建锁和同步器提供了一些通用功能的实现,因此,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock
,Semaphore
,其他的诸如 ReentrantReadWriteLock
,SynchronousQueue
等等皆是基于 AQS 的。
原理:
AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制。因此aqs中有一个链表实现的阻塞队列,并将线程封装成队列的结点,头结点是拿到了锁的线程节点。因此释放锁时,会唤醒头结点之后的第一个有效结点。
锁的状态用了status的int变量表示,0表示无锁,1为有锁,大于1则是锁重入的次数,释放锁之后,stats的值应该是0.
并且AQS是利用了Volatile和CAS实现多线程抢锁,state,head,tail都是用volatile修辞的变量,比如尾插节点的时候,修改state变量的时候需要用到AQS。
AQS不仅可以实现互斥锁还可以实现共享锁。AQS采用的是模版方法设计模式,如果实现互斥锁则需要重写tryaquire,tryrelease,如果实现共享锁,则需要重写tryaquire_shared和tryrelease。
AQS还提供了条件变量,也就是允许线程在满足某些条件之前等待,并且在其他线程满足条件时唤醒等待这个条件的线程,并且还维护了一个条件队列,当唤醒等待条件的线程时,修改线程节点的状态为可唤醒状态,然后将等待队列中的节点从条件等待队列中转移到同步队列中,以便被唤醒后能够获得锁。
5.同步器
原理解读
模板方法设计模式,如何抢锁和释放锁由子类实现。
从获取锁的逻辑开始:
acquire方法
public final void acquire(int arg) {
// 如果获取不到锁,则先把该线程结点加入到阻塞队列中,(互斥锁)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter方法
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
// 可能有很多个线程竞争在一个队列的坑位,
// 那么先把你们的前驱都指向尾节点,线程通过CAS竞争,让tail的next的指向自己。
node.prev = pred;
// CAS设置尾结点
if (compareAndSetTail(pred, node)) {
// 竞争到了,直接返回自己
pred.next = node;
return node;
}
}
// 没有竞争到的,就继续循环竞争
enq(node);
return node;
}
enq方法
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;
}
}
}
}
acquireQueued方法
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);
}
}
shouldParkAfterFailedAcquire方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
// 有效,就返回
return true;
if (ws > 0) {
// 无效,找到第一个有效的
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}