2012-04-12 3 views
1

大きなコードの一部としてこのコード(Obfuscated)があり、行にNullPointerExceptionが表示されています。 isEmpty()コールをチェックしただけで、このキューをポーリングするスレッドは他にありません。これはどのように可能ですか?他のスレッドがキューに追加されています。キューを永久に追加することは可能ですか?ArrayDequeは空ではありませんが、ポーリングメソッドの場合はnullを返します

ArrayDequeのソースコードを読もうとしましたが、isEmpty()のチェックとしてhead == tailが使用されています。いくつかの奇妙な衝突が追加の間に可能ですかhead != tailheadnullを指していますか? apiで述べたように

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を使用すべきでないことを明示しています。これは並行性の問題の1つです。


彼らは、スレッドセーフではありません。外部同期がない場合、複数のスレッドによる同時アクセスをサポートしていません。


あなたがスレッドセーフQueueをしたい場合は、あなたがLinkedBlockingDequeを使用することができますDequeueが必要な場合は、LinkedBlockingQueueを使用することができます。


資源:

1

彼らはスレッドセーフではありません。外部同期がない場合はとなり、複数のスレッドによる同時アクセスはサポートされません。

+0

私は、OPが「head == tail」の単純なチェックが失敗する理由を知りたいと思っています。これは、ドキュメントの引用よりもここで必要な説明です。 –

0

active.poll()は、両端キューが同時にいっぱいになっているため、古い要素[] ArrayDequeue.doubleCapacity()で埋め立てにアクセスしたときにあなたはケースを考えることができます。

一つの可能​​なタイムライン:

  • ポーリング・スレッドが呼び出して `` `アクティブ偽active.isEmpty()戻って

    1. ポーリングスレッドをチェックします。pollFirst())activeが満杯とdoubleCapacity(であるよう
    2. つまたは複数の他のスレッドがバーストでactive.addLast()を呼び出すアトミックでない要素[]にアクセスするため)(doubleCapacityで
    3. がトリガされ、要素は[]置き換えられ古い要素[]がGCによって再利用されるように新しく割り当てられた配列で置換されます。
    4. ポーリングスレッドは現在、再生された要素[]を参照し、nullを返す可能性があります。

    私の推測では、キューが空でない間にポーリングの同期を避けたいということです。 doubleCapacity()による競合を避けるには、キューが十分に大きな容量で割り当てられ、addLast()が呼び出されたときにキューがいっぱいにならないようにしてください。しかし、実際の実装に応じて考慮する必要がある他のレースがあるかもしれません。

    次のopenJDKのソースがFYIに追加されています。

    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; 
    } 
    
  • 関連する問題