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

Java 8:并行环境下ArrayDeque元素超线程池大小及poll返回null问题

Why does ArrayDeque's size exceed 10 in this multi-threaded scenario?

Let's break down exactly what's happening here, and why your ArrayDeque is growing beyond the 10 elements you'd expect given your thread pool size.

The Core Issue: ArrayDeque is Not Thread-Safe

First and foremost, ArrayDeque is a non-thread-safe collection. None of its methods (poll(), add(), size()) are synchronized or designed to handle concurrent access without external coordination. When multiple threads modify and read the queue at the same time, race conditions emerge that break your expected behavior.

Let's Walk Through the Race Condition in Your Code

Your task logic follows this flow:

Integer item = itemsAvailable.poll();
if (item == null)
    item = new Random().nextInt();
task.run();
itemsAvailable.add(item);
System.out.println("Items available: " + itemsAvailable.size());

Here's how the queue size spirals beyond 10:

  • Imagine your queue is empty (size 0) and 3 threads start executing this code nearly simultaneously.
  • All 3 threads call itemsAvailable.poll() and get null—since there's no synchronization, all threads see the empty queue state at the same time.
  • Each thread creates a new Integer instance.
  • After their tasks finish, all 3 threads add their new Integer back to the queue.
  • Suddenly, your queue has 3 new elements instead of just 1—and this pattern repeats across different batches of threads.

Even when the queue has elements, the lack of synchronization causes issues:

  • Thread A might call poll() and remove an element, but before Thread A can add it back, Thread B calls poll() and also gets null (if the queue was down to 1 element when Thread A removed it, but Thread B hasn't seen the update yet).
  • Thread B creates another new element, leading to an extra element in the queue once both threads add their items back.

Bonus: Work Stealing Pool Behavior

While you set your work stealing pool to 10 threads with Executors.newWorkStealingPool(10), keep in mind that work stealing pools can adjust thread counts slightly in edge cases—but this isn't the main issue here. Even with a strictly fixed 10-thread pool, the non-thread-safe queue would still cause the size to exceed 10 due to race conditions.

Fixing the Problem

You have two straightforward solutions:

1. Use a Thread-Safe Deque

Replace ArrayDeque<Integer> with ConcurrentLinkedDeque<Integer>, which is purpose-built for concurrent access. All its methods handle synchronization internally:

private ConcurrentLinkedDeque<Integer> itemsAvailable = new ConcurrentLinkedDeque<>();

2. Add Explicit Synchronization

If you need to stick with ArrayDeque, wrap all queue accesses in a synchronized block (or use a Lock for more control):

// Inside your runAsync lambda:
Integer item;
synchronized (itemsAvailable) {
    item = itemsAvailable.poll();
    if (item == null)
        item = new Random().nextInt();
}
task.run();
synchronized (itemsAvailable) {
    itemsAvailable.add(item);
    System.out.println("Items available: " + itemsAvailable.size());
}

Either approach will prevent race conditions and keep your queue size aligned with the number of active threads (or at least prevent it from growing uncontrollably).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:39:21