Java 如果我只阅读收藏,我需要担心线程安全吗?

Java 如果我只阅读收藏,我需要担心线程安全吗?,java,concurrency,Java,Concurrency,我想将一个预构建的映射传递给多个线程,这些线程只会同时从映射中读取值。在这种情况下,我是否需要担心并发访问并使用ConcurrentHashMap而不是HashMap?从理论上讲,并发读取不会对集合造成问题 但是,即使修改一次,也很容易出错,然后应该使用并发版本。否,您不必担心它。 除非有人在线程访问预构建的映射时更改它。如果所有线程都在读取它,则通过HashMap是安全的。但是,在传递映射之前,我会调用Collections.unmodifiableMap(),以确保如果其中一个线程意外地修改

我想将一个预构建的映射传递给多个线程,这些线程只会同时从映射中读取值。在这种情况下,我是否需要担心并发访问并使用ConcurrentHashMap而不是HashMap?

从理论上讲,并发读取不会对集合造成问题


但是,即使修改一次,也很容易出错,然后应该使用并发版本。

,您不必担心它。

除非有人在线程访问预构建的映射时更改它。

如果所有线程都在读取它,则通过HashMap是安全的。但是,在传递映射之前,我会调用Collections.unmodifiableMap(),以确保如果其中一个线程意外地修改了它,因为将来某些程序员不遵守当前的约定,您将得到一个异常。

不,您不需要使用它。只有在以下情况下才会产生冲突:

  • 当一些线程可能会读取数据,而其他线程可能会试图覆盖它时
  • 第二种情况是每个线程都试图更新数据

当每个线程仅尝试读取值时,不会产生冲突。

此外,为了确保仅为读取操作而处理,您可以做的是返回一个

您仍然需要确保创建和初始化映射的线程与随后读取映射的线程之间存在“发生在”关系。如果没有这种关系,理论上读取线程可能会看到过时的数据

如果在启动其他线程的线程中创建并填充映射。。。在它这样做之前。。。你应该没事的

另一种确保安全的简单方法是这样做:

public class Foo {
    private final Map map;

    public Foo(....) {
        map = new HashMap();
        // populate
    }

    public Map getMap() {
        // If the 'map' is final, and nothing changes it apart from 
        // the constructor, this method doesn't need to be synchronized.
    }
这两种方法都会在创建/初始化和每个线程首次使用映射之间产生“之前发生”的关系


您还可以在上面声明
map
volatile
,但可能会导致发生比严格需要更多的缓存刷新;i、 e.您将受到性能的小影响。

您始终需要担心线程安全。在您的具体示例中,如果您

  • 在单个线程中完全构建地图
  • 通过设置引用它的
    volatile
    变量,将其发布到其他线程

  • 或者,如果这是一个选项,请确保在映射准备就绪之前不会启动线程。

    unmodifiableMap
    调用不会确保映射正确发布到其他线程。在执行命令之前不会发生任何
    。Marko问题是,如果您使映射不可修改,并在中传递不可修改的映射,那么映射是预构建的。线程如何修改映射?它仅在创建映射的线程内预构建。您似乎不熟悉Java内存模型和
    之前发生的关系,我不理解Java内存模型,我对它的含义感到非常惊讶。即使不进行修改,如果在读取和初始写入之间没有
    关系之前发生
    ,也会出现问题。是的,这是一个明确的要求,在传递散列供其他线程使用之前,初始值必须存在。如果没有特别注意确保
    发生在
    关系之前,则初始值不会存在。这是JLS定义的术语。根据具体情况,
    volatile
    可能无法避免,但正如您所指出的,如果情况允许,应避免使用。@MarkoTopolnik-您是正确的。但是,我假设“我想将预构建的映射传递给多个线程”意味着映射是在线程启动之前构建的。(以任何其他方式解释都会延伸英语……)这就是我的意思:“我想向其他线程发布一个我在发布之前已经完全构建好的地图实例”。许多人的回答与你一样,他们都错了。您忽略了Java内存模型的含义。具体地说,如果在写入和读取之间的
    关系之前不强制执行适当的
    ,就无法保证线程安全。但是它们的读写之间没有关系,因为问题清楚地表明,所有线程都只读取值。如果上次写入和任何读取之间没有
    关系,它们就不会读取正确的值。你的回答没有具体说明如何建立这种关系。