Volatile и ArrayBlockingQueue и, возможно, другие параллельные объекты
Я понимаю (или, по крайней мере, думаю, что понимаю;) ) принцип, лежащий в основе ключевого слова volatile.
При просмотре источника ConcurrentHashMap можно увидеть, что все узлы и значения объявлены volatile, что имеет смысл, поскольку значение может быть записано/прочитано из более чем одного потока:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
...
}
Однако, глядя в ArrayBlockingQueue источник, это простой массив, который обновляется/читается из нескольких потоков:
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
Как гарантировать, что значение, вставленное в items[putIndex], будет видно из другого потока, при условии, что элемент внутри массива не является изменчивым (я знаю, что объявление самого массива не оказывает никакого влияния на сами элементы)?
Не может ли другой поток содержать кэшированную копию массива?
Спасибо
2 ответа:
Обратите внимание, что
enqueueсоставляетprivate. Ищите все вызовы к нему (offer(E), offer(E, long, TimeUnit), put(E)). Обратите внимание, что каждый из них выглядит следующим образом:Таким образом, вы можете заключить, что каждый вызовpublic void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { // Do stuff. enqueue(e); } finally { lock.unlock(); } }enqueueзащищен alock.lock() ... lock.unlock()таким образом, вам не нужноvolatile, потому чтоlock.lock/unlockтакже являются барьером памяти.