Volatile和ArrayBlockingQueue以及其他并发对象

时间:2017-03-28 12:41:43

标签: java multithreading volatile java.util.concurrent

我理解(或者至少我认为我这样做;))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(我知道声明数组本身对元素无论如何都没有任何影响)他们自己) ? 另一个线程无法保存数组的缓存副本吗?

由于

2 个答案:

答案 0 :(得分:5)

请注意enqueueprivate。查找对它的所有调用(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来保护并发访问。