我理解(或者至少我认为我这样做;))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]
的值可以从另一个线程看到,假设数组中的元素不是volatile(我知道声明数组本身对元素无论如何都没有任何影响)他们自己) ?
另一个线程无法保存数组的缓存副本吗?
由于
答案 0 :(得分:5)
请注意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
的每次通话都受lock.lock() ... lock.unlock()
保护,因此您不需要volatile
,因为{{1}也是一个记忆障碍。
答案 1 :(得分:1)
根据我的理解,不需要volatile,因为所有BlockingQueue实现都已经具有与ConcurrentHashMap
不同的锁定机制。
如果你看一下Queue的公共方法,你会发现一个ReentrantLock
来保护并发访问。