Volatile 和 ArrayBlockingQueue 以及其他可能的并发对象
Volatile and ArrayBlockingQueue and perhaps other concurrent objects
我理解(或者至少我认为我理解;))volatile
关键字背后的原理。
查看 ConcurrentHashMap
源代码时,您可以看到所有节点和值都声明为 volatile
,这是有道理的,因为值可以是来自多个线程的 written/read:
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
源代码,它是一个来自多个线程的 updated/read 普通数组:
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]
中的值将从另一个线程可见,前提是数组中的元素不是易失性的(我知道声明数组本身对元素本身)?
另一个线程不能保存数组的缓存副本吗?
谢谢
请注意 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
,因为 lock.lock/unlock
也是内存障碍。
根据我的理解,不需要 volatile,因为所有 BlockingQueue 实现都已经具有锁定机制,这与 ConcurrentHashMap
不同。
如果您查看 Queue 的 public 方法,您会发现 ReentrantLock
可以保护并发访问。
我理解(或者至少我认为我理解;))volatile
关键字背后的原理。
查看 ConcurrentHashMap
源代码时,您可以看到所有节点和值都声明为 volatile
,这是有道理的,因为值可以是来自多个线程的 written/read:
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
源代码,它是一个来自多个线程的 updated/read 普通数组:
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]
中的值将从另一个线程可见,前提是数组中的元素不是易失性的(我知道声明数组本身对元素本身)?
另一个线程不能保存数组的缓存副本吗?
谢谢
请注意 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
,因为 lock.lock/unlock
也是内存障碍。
根据我的理解,不需要 volatile,因为所有 BlockingQueue 实现都已经具有锁定机制,这与 ConcurrentHashMap
不同。
如果您查看 Queue 的 public 方法,您会发现 ReentrantLock
可以保护并发访问。