使用任何java.util.concurrent类时,是否仍需要在实例上同步访问以避免差异线程之间的可见性问题?
当使用java.util.concurrent的实例时,是否有可能一个线程修改实例(即,将一个元素放在一个并发的hashmap中),后续的线程将不会看到修改?
我的问题源于这样一个事实:如果对值的访问不同步,Java内存模型允许线程缓存值而不是直接从内存中获取值。
答案 0 :(得分:3)
在java.util.concurrent包Memory Consistency Properties上,您可以检查包的Javadoc API:
所有课程的方法 java.util.concurrent及其 子包将这些保证扩展到 更高级别的同步。在 特别是:
- 在将对象放入任何对象之前的线程中的操作 并发收集 在访问或删除之后发生之前的操作 来自集合的元素 另一个线程 [...]
- “释放”同步器方法之前的操作,例如
Lock.unlock,Semaphore.release和
CountDownLatch.countDown
在成功的“获取”方法之后发生之前的行动 比如Lock.lock, Semaphore.acquire,Condition.await, 和CountDownLatch.await 另一个同步器对象 线程。
[...]
因此,此包中的类确保了并发性,利用一组类进行线程控制(Lock
,Semaphore
等)。这些类以编程方式处理发生在之前的逻辑,即管理并发线程的FIFO堆栈,锁定和释放当前和后续线程(即使用Thread.wait()
和Thread.resume()
等。
然后,(理论上)您不需要同步访问此类的语句,因为它们以编程方式控制并发线程访问。
答案 1 :(得分:0)
因为ConcurrentHashMap(例如)旨在从并发上下文中使用,所以您不需要进一步同步它。事实上,这样做可能会破坏它所引入的优化。
例如,Collections.synchronizedMap(...)表示一种使地图线程安全的方法,据我所知,它主要通过将所有调用包装在synchronized关键字中来实现。另一方面,像ConcurrentHashMap这样的东西会在集合中的元素之间创建同步的“桶”,从而实现更细粒度的并发控制,从而在大量使用时提供更少的锁争用。例如,它也可能无法锁定读取。如果你再次使用一些同步访问来包装它,你可能会破坏它。显然,你必须要小心所有对集合的访问都是syncrhronised等,这是新库的另一个优点;你不必担心(尽可能多!)。
java.lang.concurrent集合可以通过syncrhonised实现其线程安全性。在这种情况下,语言规范保证可见性。他们可以在不使用锁的情况下实现。我对此并不是很清楚,但我认为这里的可见性相同。
如果您在代码中看到丢失更新的内容,可能只是竞争条件。像ConcurrentHashpMap这样的东西会给你一个读取的最新值,而写入可能还没有被写入。这通常是在准确性和性能之间进行权衡。
重点是; java.util.concurrent东西是为了做这些事情所以我相信它确保可见性和使用volatile和/或加法同步化不应该。