【问题标题】:Is this case not need to lock when there are only 1 writer and N reader?当只有1个写者和N个读者时,这种情况不需要锁定吗?
【发布时间】:2013-08-04 13:16:16
【问题描述】:
HashMap 不是线程安全的。
在我的例子中:1 个写入线程更新HashMap,N 读取线程读取HashMap。
我发现只有 1 个坏案例:
- 读卡器 N 读到一个值 V1
- 编写器将 V1 更新到 V2
- reader N使用值V1(但现在是V2)
如果在我的业务中,我们可以容忍这种情况,还有其他不好的情况吗?
【问题讨论】:
标签:
java
multithreading
collections
【解决方案1】:
如果读取器将遍历HashMap 并同时被写入器修改,则可能会出现更严重的问题。通常修改此类集合(在 C# 或 Python 等其他语言中)会使其所有迭代器无效。
来自documentation:
请注意,此实现不同步。 如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了映射,则必须外部同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)
【解决方案2】:
您提到的情况并不是真正的问题,因为如果 HashMap 是线程安全的,则可能会发生同样的事情。如果 V2 在 V1 正在进行更改时从 HashMap 读取数据,则可能会遇到麻烦。此时HashMap的内部状态是不一致的。这可能会导致 V2 的程序在没有明显原因的情况下神秘地崩溃。
【解决方案3】:
没有正确同步有两个问题。一个是visibility andatomicity`。
假设您有地图,并且在一个线程中增加了值。比其他线程可能会看到一个陈旧的值。类似地,如果一个线程已经放置了 K、V,那么另一个线程可能看不到它。同样,迭代也会受到影响。
所以对于 safe publication , visibility 问题最好使用 ConcurrentHashMap ,它使用 lock striping 以获得更好的并发性。但是对于我上面提到的增量内容,我们需要使用外部同步。
如果我们不更改value(K) 或key (K) 并且我们只是从HashMap 插入/getting 然后将键和值类设为immutable 类,我们可以做的最好的事情。重要的是,将HashMap 声明为final。这将处理happens before 关系并且问题将消失。谢谢。
【解决方案4】:
当读者在其中搜索时,作者可以修改 HashMap 的内部结构,从而导致随机行为(错误的值、空值而不是值、异常)。此外,更改可能永远不会传播到其他线程,而不会告诉 jvm 期望多线程访问,更新永远不会在多个 cpu 内核上同步,或者 jvm 只是将它们优化为 noop。
在您的情况下,您可以使用 ReadWriteLock 来确保所有读者可以同时访问地图,而作者拥有独占访问权限。
【解决方案5】:
好吧,如果您要问在这种情况下会发生什么,那么我们首先应该了解的是,当一个类被记录为 NOT THREAD SAFE 时,所有这些坏事都可能发生,通常发生在非线程安全的情况下类。现在,非线程安全类会发生什么,它不会按照合同正确运行。这意味着如果您调用 get(Key) ,它可能不会返回正确的值。而非线程安全类的这种行为归因于 ATOMICITY 和 VISIBILTY。
底线是,如果一个类不是线程安全的,你就不能指望它的契约或暴露的特性。