并发之ReentrantLock的condition队列和CLH队列

q1871901600 发布于 2024-11-14 19 次阅读


在java的ReentrantLock中通过两个队列实现不同场景下的线程排队阻塞。

当ReentrantLock使用了公平锁时,使用CLH队列排队阻塞线程,让线程公平的排队获取线程锁。

加入阻塞队列的时机就是当获取锁失败时加入等待队列。

    /**
     * Acquires in exclusive interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    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) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

当ReentrantLock使用condition时,将会使用condition队列调度线程阻塞和唤醒线程。

条件等待队列

Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备时,这些等待线程才会被唤醒,从而重新争夺锁

AQS源码分析

//ReentrantLock中的创建Condition
public Condition newCondition() {
        return sync.newCondition();
    }

//AQS源码
  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);
        }

当condition队列中的线程被唤醒时将会从condition节点转换为阻塞队列节点。过程为:

参考文章:

深入理解Java并发框架AQS系列(五):条件队列(Condition) - 昔久 - 博客园

从ReentrantLock的实现看AQS的原理及应用 - 美团技术团队

一个会写python的Java工程师
最后更新于 2024-11-14