使用 java.util.concurrent 类时是否应该进行同步以避免可见性问题?

发布于 2024-09-29 16:08:05 字数 234 浏览 8 评论 0原文

使用任何 java.util.concurrent 类时,我是否仍然需要同步实例上的访问以避免不同线程之间的可见性问题?

进一步阐述这个问题

当使用 java.util.concurrent 的实例时,是否有可能一个线程修改该实例(即,将一个元素放入并发哈希图中)而后续线程不会看到该修改?

我的问题源于这样一个事实:如果对值的访问不同步,Java 内存模型允许线程缓存值,而不是直接从内存中获取它们。

When using any of the java.util.concurrent classes, do I still need to synchronize access on the instance to avoid visibility issues between difference threads?

Elaborating the question a bit more

When using an instance of java.util.concurrent, is it possible that one thread modify the instance (i.e., put an element in a concurrent hashmap) and a subsequent thread won't be seeing the modification?

My question arises from the fact that The Java Memory Model allows threads to cache values instead of fetching them directly from memory if the access to the value is not synchronized.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

戒ㄋ 2024-10-06 16:08:05

在 java.util.concurrent 包 内存一致性属性,您可以查看该包的Javadoc API:

所有类的方法
java.util.concurrent 及其
子包将这些保证扩展到
更高级别的同步。在
特别是:

  • 将对象放入任何线程之前执行的操作
    并发收集
    happen-before 访问或删除之后的操作
    集合中的元素
    另一个线程。
    [...]
  • “释放”同步器方法之前的操作,例如
    Lock.unlock、Semaphore.release 和
    CountDownLatch.countDown
    happen-before 成功“获取”方法之后的操作
    比如Lock.lock,
    信号量.获取,条件.等待,
    和 CountDownLatch.await 上
    另一个中的相同同步器对象
    线程。
    [...]

因此,这个包中的类确保了并发性,利用一组类进行线程控制(锁定信号量等)。此类以编程方式处理发生前逻辑,即管理并发线程的 FIFO 堆栈、锁定和释放当前和后续线程(即使用 Thread.wait() 和 < code>Thread.resume() 等。

然后,(理论上)您不需要同步访问此类的语句,因为它们以编程方式控制并发线程访问。

On the java.util.concurrent package Memory Consistency Properties, you can check the Javadoc API for the package:

The methods of all classes in
java.util.concurrent and its
subpackages extend these guarantees to
higher-level synchronization. In
particular:

  • Actions in a thread prior to placing an object into any
    concurrent collection
    happen-before actions subsequent to the access or removal of that
    element from the collection in
    another thread.
    [...]
  • Actions prior to "releasing" synchronizer methods such as
    Lock.unlock, Semaphore.release, and
    CountDownLatch.countDown
    happen-before actions subsequent to a successful "acquiring" method
    such as Lock.lock,
    Semaphore.acquire, Condition.await,
    and CountDownLatch.await on the
    same synchronizer object in another
    thread.
    [...]

So, the classes in this package make sure of the concurrency, making use of a set of classes for thread control (Lock, Semaphore, etc.). This classes handle the happen-before logic programmatically, i.e. managing a FIFO stack of concurrent threads, locking and releasing current and subsequent threads (i.e. using Thread.wait() and Thread.resume(), etc.

Then, (theoretically) you don't need to synchronize your statements accessing this classes, because they are controlling concurrent threads access programmatically.

昔梦 2024-10-06 16:08:05

因为 ConcurrentHashMap(例如)被设计为在并发上下文中使用,所以您不需要进一步同步它。事实上,这样做可能会破坏它引入的优化。

例如,Collections.synchronizedMap(...) 代表一种使映射线程安全的方法,据我了解,它本质上是通过将所有调用包装在synchronized 关键字中来工作的。另一方面,像 ConcurrentHashMap 这样的东西会在集合中的元素之间创建同步的“桶”,从而导致更细粒度的并发控制,从而在大量使用的情况下减少锁争用。例如,它也可能不会锁定读取。如果你用一些同步访问再次包装它,你可能会破坏它。显然,您必须小心,对集合的所有访问都是同步的等,这是新库的另一个优点;你不必担心(同样多!)。

java.lang.concurrent 集合可以通过同步来实现线程安全。在这种情况下,语言规范保证了可见性。他们可以在不使用锁的情况下实现一些东西。我对此不太清楚,但我认为这里也会有相同的可见性。

如果您在代码中看到看起来丢失更新的内容,则可能只是一个竞争条件。像 ConcurrentHashpMap 这样的东西将为您提供读取时的最新值,而写入可能尚未写入。这通常是准确性和性能之间的权衡。

重点是; java.util.concurrent 的东西就是用来做这些事情的,所以我相信它确保了可见性,并且不需要使用易失性和/或附加同步。

Because the ConcurrentHashMap (for example) is designed to be used from a concurrent context, you don't need to synchronise it further. In fact, doing so could undermine the optimisations it introduces.

For example, Collections.synchronizedMap(...) represents a way to make a map thread safe, as I understand it, it works essentially by wrapping all the calls within the synchronized keyword. Something like ConcurrentHashMap on the other hand creates synchronized "buckets" across the elements in the collection, causing finer grained concurrency control and therefore giving less lock contention under heavy usage. It may also not lock on reads for example. If you wrap this again with some synchronised access, you could undermine this. Obviously, you have to be careful that all access to the collection is syncrhronised etc which is another advantage of the newer library; you don't have to worry (as much!).

The java.lang.concurrent collections may implement their thread safety via syncrhonised. in which case the language specification guarantees visibility. They may implement things without using locks. I'm not as clear on this, but I assume it the same visibility would be in place here.

If you're seeing what looks like lost updates in your code, it may be that its just a race condition. Something like the ConcurrentHashpMap will give you the most recent value on a read and the write may not have yet been written. It's often a trade off between accuracy and performance.

The point is; java.util.concurrent stuff is meant to do this stuff so I'd be confident that it ensures visibility and use of volatile and/or addition syncrhonisation shouldn't be needed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文