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

while循环轮询阻塞队列场景下的多线程安全优化方案咨询

有两种性能更优的无锁解决方案,均优于直接加synchronized悲观锁:

方案1:调整拉取逻辑,允许多线程并行处理(最高性能)

LinkedBlockingQueue本身的poll()操作是线程安全的原子操作,原代码的问题出在isEmpty()判断和poll()操作拆分执行,两者组合不是原子操作。直接去掉前置的isEmpty()判断,以poll()的返回值作为循环终止条件即可完全规避null问题,全程无锁,性能最优。

修改后的createJob()代码如下:

public CompoundJob createJob() {
    CompoundJob job = new CompoundJob();
    Integer itemId;
    // 直接循环拉取,poll返回null即代表队列已空
    while ((itemId = idsOfItemsToDelete.poll()) != null) {
        job.addSubAssignment(DeletionAssignment.of(itemId));
    }
    return job;
}

该方案适合队列经常有大量积压的场景:多个工作线程可以同时拉取队列元素生成多个作业并行执行,加快积压任务的处理速度,不会出现重复拉取、null入任务的问题。

方案2:CAS独占标记,单线程拉取整批任务

如果业务要求每次定时调度生成的作业是全量待删条目,不希望拆分给多个作业并行处理,可以用AtomicBoolean实现轻量的乐观锁,同一时间仅允许一个线程拉取全量队列生成作业,抢不到锁的线程直接返回空作业即可,没有synchronized锁的线程挂起、上下文切换开销。

修改后的ItemDeletionManager代码如下:

class ItemDeletionManager {
    private final Queue<Integer> idsOfItemsToDelete;
    // CAS独占标记,标记是否有线程正在生成作业
    private final AtomicBoolean jobCreatingFlag = new AtomicBoolean(false);

    public ItemDeletionManager() {
        this.idsOfItemsToDelete = new LinkedBlockingQueue<>();
    }

    public void enqueue(int itemId) {
        this.idsOfItemsToDelete.add(itemId);
    }

    public CompoundJob createJob() {
        CompoundJob job = new CompoundJob();
        // 抢锁失败直接返回空作业,不阻塞
        if (!jobCreatingFlag.compareAndSet(false, true)) {
            return job;
        }
        try {
            Integer itemId;
            while ((itemId = idsOfItemsToDelete.poll()) != null) {
                job.addSubAssignment(DeletionAssignment.of(itemId));
            }
            return job;
        } finally {
            // 必须在finally中重置标记,避免异常导致标记永久占用
            jobCreatingFlag.set(false);
        }
    }
}

如果你的业务量级不大,其实直接把定时线程池的核心线程数从2改成1,从根源避免多线程竞争,是成本最低的解决方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:36:03