1

我有这个代码(混淆)作为大型应用程序的一部分,它正在NullPointerExceptionobject.doSomething()线。既然我们只是检查了isEmpty()调用,没有其他线程轮询这个队列,这怎么可能呢?还有其他线程添加到队列中;并发添加是否可能永久搞砸队列?

我尝试阅读源代码,ArrayDequehead == tail用作检查isEmpty(). 在添加过程中是否可能发生一些奇怪的碰撞,head != tailhead指向null

private final Queue<Task> active = new ArrayDeque<Task>();
if (!this.active.isEmpty()) {
    SomeType object = null;
    object = this.active.poll();
    object.doSomething();
}
4

3 回答 3

3

即使没有其他线程轮询,也可能有其他线程在推送。

这意味着在并发访问中,tail 可能会被错误地修改,如果 tail 被损坏,您可能永远无法head == tail到达NullPointerException.

正如@dacwe 所说,文档明确指出您(或此混淆应用程序的开发人员)不应ArrayDeque在并发环境中使用,这是并发可能出现的问题之一。


它们不是线程安全的;在没有外部同步的情况下,它们不支持多线程并发访问。


如果你想要一个线程安全的Queue你可以使用LinkedBlockingQueue,如果你需要一个Dequeue你可以使用LinkedBlockingDeque


资源:

于 2012-04-12T15:30:30.763 回答
1

api所述:

它们不是线程安全的;在没有外部同步的情况下,它们不支持多线程并发访问。

于 2012-04-12T15:24:23.397 回答
0

您可以考虑访问同时回收双端队列已满active.poll()的旧元素[]的情况。ArrayDequeue.doubleCapacity()

一种可能的时间表:

  1. 轮询线程签出active.isEmpty()返回 false
  2. 轮询线程调用```active.pollFirst() 来访问非原子元素[]
  3. 一个或多个其他线程active.addLast()突发调用,因此active已满并触发 doubleCapacity()
  4. 在 doubleCapacity() 中,elements[] 被新分配的数组替换,这样旧的 elements[] 被 GC 回收
  5. 轮询线程现在引用回收的元素 [] 并可能变为空。

我的猜测是,当队列不为空时,您希望避免同步轮询。为避免由于 doubleCapacity() 导致的竞争,请确保为队列分配了足够大的容量,并且在调用 addLast() 时不会满。但是,根据实际实施,您可能需要考虑其他竞赛。

以下来自 openJDK 的源代码附在后面,仅供参考。

public E pollFirst() {
    int h = head;
    @SuppressWarnings("unchecked")
    E result = (E) elements[h];
    // Element is null if deque empty
    if (result == null)
        return null;
    elements[h] = null;     // Must null out slot
    head = (h + 1) & (elements.length - 1);
    return result;
}

public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    elements[tail] = e;
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity();
}

private void doubleCapacity() {
    assert head == tail;
    int p = head;
    int n = elements.length;
    int r = n - p; // number of elements to the right of p
    int newCapacity = n << 1;
    if (newCapacity < 0)
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity];
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    tail = n;
}
于 2016-08-05T20:07:45.387 回答