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

使用ArrayBlockingQueue的removeIf()后,put()为何未触发?

问题分析:ArrayBlockingQueue多线程下removeIf后put阻塞未唤醒

问题场景

在多线程环境下调用ArrayBlockingQueue的removeIf()方法移除元素后,另一个执行put()的线程始终阻塞,无法在队列腾出空间后继续插入元素。程序输出前两个队列大小后,第三个输出一直无法出现,t3线程的put(1)持续等待。

复现代码

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CyclicBarrier;

class HelloWorld {
    
    static ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue(2);
    static CyclicBarrier cb = new CyclicBarrier(2);

    public static void main(String[] args) {
        try {
            Thread t1 = new Thread(() -> {
                try {
                    q.put(2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            Thread t2 = new Thread(() -> {
                try {
                    q.put(2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            Thread t3 = new Thread(() -> {
                try {
                    q.put(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            t1.start();
            t2.start();
            t3.start();
            t1.join();
            t2.join();
            System.out.println(q.size());
            q.removeIf(ii -> ii == 2);
            System.out.println(q.size());
            t3.join();
            System.out.println(q.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

问题原因

  1. removeIf未触发条件通知:ArrayBlockingQueue的removeIf()方法继承自AbstractQueue,属于通用集合方法,不会触发队列内部的notFull条件变量唤醒机制。而put()方法阻塞时,是等待notFull条件的信号;移除元素后没有通知这个条件,阻塞线程无法感知到队列已腾出空间。
  2. 执行时序问题:t1、t2先填满队列(容量2),t3的put()立刻进入阻塞。主线程清空队列后,没有唤醒等待插入的线程,导致t3一直阻塞,t3.join()永远无法完成。

解决方案

方案一:使用队列重写的remove方法(推荐)

替换removeIf()为循环调用ArrayBlockingQueue重写的remove(Object)方法,该方法在移除元素后会自动调用notFull.signal()唤醒等待插入的线程:

// 替换原有的q.removeIf(ii -> ii == 2);
while (q.remove(Integer.valueOf(2))) {}

方案二:手动唤醒notFull条件(不推荐,破坏封装)

如果必须使用removeIf(),可以通过反射获取队列的notFull条件变量并手动唤醒,但这种方式依赖内部实现,兼容性差:

q.removeIf(ii -> ii == 2);
// 反射唤醒notFull条件
try {
    Field notFullField = ArrayBlockingQueue.class.getDeclaredField("notFull");
    notFullField.setAccessible(true);
    Condition notFull = (Condition) notFullField.get(q);
    notFull.signalAll();
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

验证结果

使用方案一修复后,程序会依次输出2、0、1,t3线程的put()会正常执行,主线程也能顺利完成t3.join()并输出最终队列大小。

内容的提问来源于stack exchange,提问作者Jia Wen Chin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:16:11