【问题标题】:Volatile and ArrayBlockingQueue and perhaps other concurrent objectsVolatile 和 ArrayBlockingQueue 可能还有其他并发对象
【发布时间】:2017-03-28 12:41:42
【问题描述】:

我理解(或者至少我认为我理解;))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] 中的值将从另一个线程可见,前提是数组内的元素不是易失性的(我知道声明数组本身对元素本身没有任何影响) ? 其他线程不能保存数组的缓存副本吗?

谢谢

【问题讨论】:

    标签: java multithreading volatile java.util.concurrent


    【解决方案1】:

    注意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,因为lock.lock/unlock 也是一个内存屏障。

    【讨论】:

    • 是的,我注意到了,但我不知道锁定是内存屏障。我认为同步可以,但不确定锁定。你确定吗?因为 lock() 唯一要做的就是将当前线程标记为独占所有者并设置内部状态。我看不出它与内存屏障有什么关系
    • 好吧,你是对的,在 java 文档中它说锁必须提供与同步相同的内存语义。所以我想知道它是如何在内部工作的。可能是编译器对这些锁有特定的处理?
    【解决方案2】:

    根据我的理解,不需要 volatile,因为所有 BlockingQueue 实现都已经具有与 ConcurrentHashMap 不同的锁定机制。 如果您查看 Queue 的公共方法,您会发现 ReentrantLock 用于保护并发访问。

    【讨论】:

      猜你喜欢
      • 2014-08-23
      • 2019-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多