2012-04-12 52 views
1

我有這個代碼(混淆)作爲一個大型應用程序的一部分,它得到NullPointerExceptionobject.doSomething()線上。由於我們剛剛檢查了isEmpty()調用,並且沒有其他線程正在輪詢此隊列,這怎麼可能?還有其他線程添加到隊列中;是否有可能同時添加永久搞砸隊列?ArrayDeque不爲空,但返回爲空查詢方法

我嘗試閱讀ArrayDeque的源代碼,它使用head == tail作爲isEmpty()的檢查。在添加head != tail期間是否可能發生一些奇怪的碰撞,但是head指向null

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

回答

3

即使沒有其他線程輪詢,也可能有其他線程推送。

這意味着在併發訪問中,尾部可以被錯誤地修改,並且如果尾部被損壞,則可能永遠不會到達head == tail,因此NullPointerException

正如@dacwe所述,文檔明確指出您(或此混淆應用程序的開發人員)不應在併發環境中使用ArrayDeque,這是併發性的可能問題之一。


它們不是線程安全的;在沒有外部同步的情況下,它們不支持多線程的併發訪問。


如果你想有一個線程Queue你可以使用LinkedBlockingQueue,如果你需要一個Dequeue可以使用LinkedBlockingDeque


資源:

1

正如api說:

他們不是線程安全的;在沒有外部同步的情況下,它們不支持多線程的併發訪問。

+0

我認爲OP已經知道,他只是想知道爲什麼簡單檢查'head == tail'可能會失敗。這裏所需的解釋比文檔中的引用要多。 – 2012-04-12 15:26:06

0

active.poll()訪問舊元素[]回收在ArrayDequeue.doubleCapacity()爲其雙端隊列已滿,同時你可以考慮的情況下。

一個可能的時間表:

  1. 輪詢線程檢查出active.isEmpty()返回false
  2. 輪詢線程調用```活躍。pollFirst()來訪問的元素[]這是不原子
  3. 一個或多個其它線程在突發調用active.addLast()使得active充滿和doubleCapacity()被觸發
  4. 在doubleCapacity(),元素[]被替換使用新分配的數組,使舊回收的元素[]回收到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; 
}