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

Java是否存在并发循环链表队列?如何实现高并发轮询循环队列

好问题!Java标准库确实没有提供直接的并发循环链表队列实现,但结合你描述的场景——初始化阶段多线程添加元素,运行阶段只读且多线程循环消费——我们可以有几种高效的实现方式,下面分情况说明:

一、针对你的场景的最优方案(初始化后只读)

因为你的队列在运行阶段不再修改元素,这是一个关键的优化点,我们可以利用不可变数组+原子索引来实现极致的性能:

思路

  1. 初始化阶段:等待所有线程完成元素添加后,将元素拷贝到一个不可变数组中(保证线程可见性,比如通过构造方法一次性传入已完成初始化的数组)。
  2. 消费阶段:每个线程通过原子类AtomicInteger维护当前遍历的索引,每次获取元素后将索引原子性加1并取模数组长度,实现循环轮询。

代码示例

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class ConcurrentCircularPoller<T> {
    private final T[] elements;
    private final AtomicInteger currentIndex = new AtomicInteger(0);

    // 确保传入的数组是已经完成初始化的(所有元素已添加完毕)
    public ConcurrentCircularPoller(T[] elements) {
        // 复制数组,避免外部修改影响内部状态
        this.elements = Arrays.copyOf(elements, elements.length);
    }

    public T next() {
        if (elements.length == 0) {
            throw new IllegalStateException("No elements available to poll");
        }
        // 原子更新索引:当前索引+1后取模数组长度,实现循环
        int index = currentIndex.getAndUpdate(i -> (i + 1) % elements.length);
        return elements[index];
    }
}

初始化阶段的线程安全处理

如果初始化阶段是多线程添加元素,可以用CountDownLatch来等待所有线程完成添加,再构造ConcurrentCircularPoller:

// 示例初始化流程
ConcurrentLinkedQueue<T> tempQueue = new ConcurrentLinkedQueue<>();
CountDownLatch latch = new CountDownLatch(3); // 假设3个初始化线程

// 初始化线程
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 添加元素到tempQueue
        tempQueue.add(...);
        latch.countDown();
    }).start();
}

latch.await(); // 等待所有初始化线程完成
// 将队列转为数组,构造循环轮询器
ConcurrentCircularPoller<T> poller = new ConcurrentCircularPoller<>(tempQueue.toArray(new T[0]));

这个方案的优势是无锁、O(1)时间复杂度的元素获取,性能远超基于链表的实现,完全满足你的高性能需求。

二、基于ConcurrentLinkedQueue的循环改造方案(支持动态添加元素)

如果之后有动态添加元素的需求,我们可以包装ConcurrentLinkedQueue,通过迭代器遍历到末尾后重新获取迭代器来实现循环:

代码示例

import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentCircularQueue<T> {
    private final ConcurrentLinkedQueue<T> queue;

    public ConcurrentCircularQueue(ConcurrentLinkedQueue<T> queue) {
        this.queue = queue;
    }

    public T next() {
        if (queue.isEmpty()) {
            throw new IllegalStateException("Queue is empty");
        }
        Iterator<T> iterator = queue.iterator();
        while (true) {
            if (iterator.hasNext()) {
                return iterator.next();
            }
            // 遍历到队尾,重新获取迭代器从头开始
            iterator = queue.iterator();
        }
    }
}

说明

  • ConcurrentLinkedQueue的迭代器是弱一致性的,意味着迭代过程中如果有元素添加,迭代器可能不会立即看到,但你的场景中运行阶段不再添加元素,所以完全不会有问题。
  • 这个方案的性能比数组方案差一些,因为每次遍历到队尾需要重新创建迭代器,但胜在支持动态修改队列。

总结

结合你的实际场景(初始化后只读),优先选择数组+原子索引的方案,它的性能最高且实现简单。如果之后有动态修改的需求,再考虑基于ConcurrentLinkedQueue的包装实现。

内容的提问来源于stack exchange,提问作者Ihor M.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:10:02