1

私はこのコード(難読化)を大規模なアプリケーションの一部として持ってNullPointerExceptionおり、object.doSomething()オンラインになっています。呼び出しをチェックしたばかりでisEmpty()、このキューをポーリングする他のスレッドがないので、これはどのように可能ですか?キューに追加している他のスレッドがあります。同時追加がキューを永久に台無しにする可能性はありますか?

のソースコードを読んでみましたが、のチェックとしてArrayDeque使用しています。追加中に奇妙な衝突が発生する可能性はありますが、ポイントはありますか? head == tailisEmpty()head != tailheadnull

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

他のスレッドのポーリングがない場合でも、おそらく他のスレッドがプッシュしています。

これは、同時アクセスでは、テールが誤って変更される可能性があることを意味します。テールが破損していると、がhead == tail、したがって、に到達しない可能性がありますNullPointerException

@dacweが述べたように、ドキュメントには、あなた(またはこの難読化されたアプリケーションの開発者)がArrayDeque並行環境で使用してはならないことが明確に指定されています。これは並行性で発生する可能性のある問題の1つです。


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


スレッドセーフが必要な場合はをQueue使用できLinkedBlockingQueue、必要なDequeue場合はを使用できますLinkedBlockingDeque


資力:

于 2012-04-12T15:30:30.763 に答える
1

APIで述べられているように:

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

于 2012-04-12T15:24:23.397 に答える
0

dequeが同時にいっぱいになっている、active.poll()再利用された古いelements[]にアクセスする場合を考えることができます。ArrayDequeue.doubleCapacity()

1つの可能なタイムライン:

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

私の推測では、キューが空でない間は、ポーリングの同期を避けたいと思います。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 に答える