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
также являются барьером памяти.