关于AQS的理解

AQS框架
AQS中维护了一个volatile int state(表示共享资源) 、一个FIFO队列(多线程竞争时,被阻塞会进入此队列)、一个condition队列
state表示共享资源,如果大于0表示当前资源正被某个线程使用,如果为0表示没有线程使用该共享资源。
关于AQS的理解
当多线程访问共享资源(state)时,流程如下:

  1. 当线程1、2、3通过cas获取state时,如果线程1获取到了资源的使用权,令当前锁的owenerThread设置为当前线程,state+1;
  2. 线程2、3未获取到共享资源,将会被加入等待队列(CLH 双端链表队列)
  3. 当线程1释放state时,令当前锁的ownerThread设置为null,state–
  4. 这里要根据是否是公平锁来竞争资源,如果是公平锁,将会按照等待队列的顺序依次获取资源。
  5. 如果不是公平锁,等待队列中的第一个线程将会和新进来的线程竞争获取资源。

以上流程中,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队列中的线程,加入到等待队列参与锁资源的竞争(是非公平锁还是公平锁)。

总结

  1. Condition 和 AQS 有什么关系?
    Condition是基于AQS实现的,Condition的出现有利于线程之间的协作。
  2. Condition 的实现原理是什么?
    Condition 内部维护一个条件队列,在获取锁的情况下,线程调用 await,线程会被放置在条件队列中并被阻塞。直到调用 signal、signalAll 唤醒线程,此后线程唤醒,会放入到 AQS 的同步队列,参与争抢锁资源。
  3. Condition 的等待队列和 AQS 的同步队列有什么区别和联系?
    Condition 的等待队列是单向链表,AQS 的是双向链表。二者之间并没有什么明确的联系。仅仅在节点从阻塞状态被唤醒后,会从等待队列挪到同步队列中(这里会根据是公平的锁还是非公平锁来决定是否插队)。
  4. 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原理
图解AQS
java并发

上一篇:672. Bulb Switcher II


下一篇:1-断点调试