dequeが同時にいっぱいになっている、active.poll()
再利用された古いelements[]にアクセスする場合を考えることができます。ArrayDequeue.doubleCapacity()
1つの可能なタイムライン:
- チェックアウトするポーリングスレッド
active.isEmpty()
はfalseを返します
- ポーリングスレッドは```active.pollFirst()を呼び出して、アトミックではない要素[]にアクセスします
- 1つ以上の他のスレッド
active.addLast()
がバーストで呼び出して、active
満杯になり、doubleCapacity()がトリガーされます
- doubleCapacity()では、elements []は新しく割り当てられた配列に置き換えられ、古いelements[]はGCによって再利用されます。
- ポーリングスレッドは、再利用された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;
}