应该在读取时对共享资源(可变)进行同步吗?

huangapple go评论70阅读模式
英文:

Should I synchronise on shared resource (mutable) while reading?

问题

我有一个HashMap,它被多个线程共享。任何线程都可以更新它。所以我在更新它时对HashMap对象进行同步。

在读取时是否应该同步?还是仅仅将其声明为volatile就足够了?

**注意:**我知道我可以简单地使用ConcurrentHashMap,但我想知道多线程是否会影响可变的共享资源的读取。如果是,它会如何影响

还有,如果它是一个简单的对象(而不是集合)或一个原始类型呢?

英文:

I have a HashMap which is shared among multiple threads. And any thread can update it. So I synchronise on HashMap object while updating it.

Should I synchronise while reading too ? OR just making it volatile is enough ?

Note: I know I could simply use ConcurentHashMap but want to know whether multi threading affects reading of shared resources which are mutable . If yes, how it affects ?

Also what if it is a simple object (not a collection) or a primitive type.

答案1

得分: 1

你需要在阅读时进行同步,或者切换到使用ConcurrentHashMap。

如果一个线程在另一个线程正在更新地图时调用get方法,那么很难预测会发生什么。get方法可能会抛出异常,因为它假定地图的内部结构在其运行时不会发生更改。

英文:

You need to synchronise while reading too, or switch to using a ConcurrentHashMap.

If one thread calls the get method while another thread is updating the map, it is impossible to predict what will happen. The get method may throw an exception, because it assumes that the internal structure of the map does not change while it's running.

答案2

得分: 1

Java HashMap documentation 中指出(原文中已加粗):

> 注意,此实现不是线程同步的。 如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,那么必须在外部进行同步。 (结构修改是指添加或删除一个或多个映射的任何操作;仅仅更改已经包含在实例中的键关联的值不是结构修改。)通常通过在自然封装映射的某个对象上同步来实现这一点。如果没有这样的对象存在,应该使用 Collections.synchronizedMap 方法“包装”映射。最好在创建时执行此操作,以防止意外的非同步访问映射:
>
> Map m = Collections.synchronizedMap(new HashMap(...));
>
> 此类的所有“集合视图方法”返回的迭代器都是 快速失败的:如果在迭代器创建之后的任何时间内以除了迭代器自身的 remove 方法之外的任何方式对映射进行结构性修改,迭代器将抛出 ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速且干净地失败,而不是在未来的某个不确定时间冒险发生任意的非确定性行为。
>
> 请注意,由于在未同步的并发修改存在的情况下,通常来说不可能做出硬性保证,因此迭代器的快速失败行为不能被保证。快速失败迭代器尽力在最佳情况下抛出 ConcurrentModificationException。因此,编写依赖于此异常来确保正确性的程序是错误的:迭代器的快速失败行为只应用于检测错误。

在读取 HashMap 时,不能安全地对其进行更改。当梯子的横档被移除时,你能安全地爬上梯子吗?

英文:

The Java HashMap documentation states (bolding in the original):

> Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:
>
> Map m = Collections.synchronizedMap(new HashMap(...));
>
> The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
>
> Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

You can not safely change a HashMap while reading from it. Can you safely climb a ladder while rungs are being removed?

答案3

得分: 0

你还应该同步读取部分,因为任何保证(在你的情况下是可见性)仅在线程使用相同的锁时才适用,所以当你在读取时跳过同步时,这意味着读取线程不使用相同的锁(它不使用任何锁),这将导致读取线程看到过时的值。请参阅此处:https://stackoverflow.com/questions/11459543/should-getters-and-setters-be-synchronized

英文:

You should also synchronize the read side because any guarantees (visibility in your case) applies only when threads uses same lock, so when you skip synchronizing while reading - it means that reading thread doesn't use the same lock (it doesn't use any lock) which will lead to see stale values by reading thread. See here: https://stackoverflow.com/questions/11459543/should-getters-and-setters-be-synchronized

huangapple
  • 本文由 发表于 2020年8月7日 19:26:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/63300860.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定