You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于ArrayBlockingQueue.take方法多线程锁状态的疑问

关于ArrayBlockingQueue.take()中多线程同时进入await()的疑问

我在阅读ArrayBlockingQueue的take()方法源码时遇到了一个困惑。我原本以为当两个线程并发调用take()方法时,只有一个线程能成功获取锁,另一个会在lock.lockInterruptibly();处等待锁。

take()方法源码如下:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

但当我对这两个线程做线程dump时,发现它们居然都成功获取了锁,并且都卡在notEmpty.await();处等待(此时队列是空的)。线程dump信息如下:

"test-thread-18" #6357 daemon prio=5 os_prio=0 tid=0x00007f8f54543000 nid=0x58ef waiting on condition [0x00007f901bc70000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)

  • parking to wait for <0x00007f93ae695410> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"test-thread-17" #6356 daemon prio=5 os_prio=0 tid=0x00007f8f54542000 nid=0x58ee waiting on condition [0x00007f901beb9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)

  • parking to wait for <0x00007f93ae695410> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

请问为什么两个不同的线程可以同时获取同一把锁?我的理解哪里出错了?


这其实是你对Condition.await()方法的机制理解有偏差啦,核心点在于:当线程调用Condition.await()时,会自动释放持有的锁,让其他线程有机会获取锁进入临界区。

具体到你的场景:

  • 第一个线程调用take(),成功获取ReentrantLock,然后发现队列是空的,调用notEmpty.await()。这时候,该线程会把锁释放掉,然后进入条件队列等待被唤醒。
  • 第二个线程此时就能顺利获取到同一把ReentrantLock,同样检查队列是空的,也调用notEmpty.await(),同样释放锁,进入条件队列等待。

这样就出现了dump里看到的两个线程都在await()处等待的情况——它们都曾经获取过锁,但在调用await后都主动释放了,所以不会出现锁竞争的问题。

等后续有线程往队列里放入元素时,会调用notEmpty.signal()或signalAll(),唤醒条件队列里的线程,被唤醒的线程会重新去竞争锁,拿到锁之后才能继续执行dequeue()操作。

总结一下:Condition.await()的设计就是让线程在等待条件满足时,释放锁让其他线程可以进入,避免持有锁等待造成的资源浪费,这是并发包里条件变量的核心机制之一。

内容的提问来源于stack exchange,提问作者Expressway Retrograding

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:08:34