关于AQS的理解
原创
©著作权归作者所有:来自51CTO博客作者呼声很高的原创作品,请联系作者获取转载授权,否则将追究法律责任
AQS框架
AQS中维护了一个volatile int state(表示共享资源) 、一个FIFO队列(多线程竞争时,被阻塞会进入此队列)、一个condition队列
state表示共享资源,如果大于0表示当前资源正被某个线程使用,如果为0表示没有线程使用该共享资源。
当多线程访问共享资源(state)时,流程如下:
- 当线程1、2、3通过cas获取state时,如果线程1获取到了资源的使用权,令当前锁的owenerThread设置为当前线程,state+1;
- 线程2、3未获取到共享资源,将会被加入等待队列(CLH 双端链表队列)
- 当线程1释放state时,令当前锁的ownerThread设置为null,state–
- 这里要根据是否是公平锁来竞争资源,如果是公平锁,将会按照等待队列的顺序依次获取资源。
- 如果不是公平锁,等待队列中的第一个线程将会和新进来的线程竞争获取资源。
以上流程中,ReentrantLock这个实现了AQS的方法,而AQS仅仅是抽象类。以上流程未说明可重入和非可重入的问题
Condition.await()和Condition.singal()实现原理。
Condition.await()和Condition.singal()与Object.wait()和Object.notify()方法类似,都是用来进行线程之间的协作。ReentrantLock为condition创建了一个condition队列,将调用了await的线程,添加到condition队列中。当某个线程调用Condition.singal()时,会从condition队列中线程放入到同步队列,参与争抢锁资源。
Condition.await()的源码
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
rivate Node addConditionWaiter() {
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
其实就是将调用await的线程,以及waitStatus,封装成一个node节点,然后加入condition队列中。
Condition.singal()的源码
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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;
}
}
}
}
调用 Condition.singal()会将condition队列中的线程,加入到等待队列参与锁资源的竞争(是非公平锁还是公平锁)。
总结
- Condition 和 AQS 有什么关系?
Condition是基于AQS实现的,Condition的出现有利于线程之间的协作。 - Condition 的实现原理是什么?
Condition 内部维护一个条件队列,在获取锁的情况下,线程调用 await,线程会被放置在条件队列中并被阻塞。直到调用 signal、signalAll 唤醒线程,此后线程唤醒,会放入到 AQS 的同步队列,参与争抢锁资源。 - Condition 的等待队列和 AQS 的同步队列有什么区别和联系?
Condition 的等待队列是单向链表,AQS 的是双向链表。二者之间并没有什么明确的联系。仅仅在节点从阻塞状态被唤醒后,会从等待队列挪到同步队列中(这里会根据是公平的锁还是非公平锁来决定是否插队)。 - Condition.singal() await()和Object.wait notify有什么区别?
Object.wait notify必须和synchronized关键字一起使用,并且只能唤醒一个(notify)或者唤醒全部(notifyall)。而Condition.singal() await()对不同条件进行控制,比如notFull。
AQS框架和Monitor框架像不像?
AQS框架和Monitor框架本质上都是基于管程实现。
Monitor底层也是有两个队列(EntryList 和waitSet队列)、recursion(计数器和AQS的state一样)、owner组成
AQS底层也是两个队列(LCH和condition队列)、state、ownerThread组成
总体来说synchronized底层是monitor而ReentrantLock的底层是AQS,他们底层都是基于管程模型建立的。monitor的设计和AQS是相似的
参考
condition原理图解AQSjava并发白话AQS