易失性hashmap的特征

时间:2014-03-06 01:48:57

标签: java multithreading volatile

我正在努力确定变量如何声明为

private volatile HashMap<Object, ArrayList<String>> data;

会在多线程环境中运行。

我的理解是volatile表示从主内存而不是从线程缓存中获取。这意味着如果正在更新变量,我将不会看到新值,直到更新完成并且我不会阻止,而是我看到的是最后更新的值。 (这是完全我想要的BTW。)

我的问题是当我检索ArrayList<String>并在线程A读取时在线程A中添加或删除字符串时,volatile关键字究竟会受到什么影响?仅HashMap或者效果是否也扩展到HashMap的内容(K和V)?也就是说,当线程B获得当前正在线程A中修改的ArrayList<String>时,实际返回的是在更新开始之前存在的ArrayList<String>的最后一个值。

为了清楚起见,我们假设更新正在添加2个字符串。当线程B获得数组时,线程A中已添加一个字符串。线程B是否在添加第一个字符串之前获取数组?

3 个答案:

答案 0 :(得分:5)

  

这意味着如果一个变量正在更新,我将不会看到新值,直到更新完成并且我不会阻止,而是我看到的是最后更新的值

这是你的困惑之源。 volatile的作用是确保对该字段的读取和写入是原子的 - 因此没有其他线程可以看到部分写入的值。

如果写入操作在写入第一个地址之后和写入第二个地址之前被抢占,则非原子长字段(在32位机器上占用2个存储器地址)可能会被错误读取。

请注意,对字段的读/写的原子性与更新HashMap的内部状态无关。更新HashMap的内部状态需要多个指令,这些指令不是原子的整体。这就是为什么你使用锁来同步HashMap的访问权限。

此外,由于对引用的读/写操作始终原子,即使该字段未标记为volatile,因此volatile和非易失性HashMap之间没有区别,关于原子性。在这种情况下,所有volatile都会为您提供获取释放语义。这意味着,即使仍允许处理器和编译器对指令进行轻微重新排序,也不会将指令移到易失性读取之上或低于易失性读取。

答案 1 :(得分:2)

此处的volatile关键字仅适用于HashMap,而不适用于存储在其中的数据,在本例中为ArrayList。

如HashMap文档中所述:

  

请注意,此实现未同步。如果有多个线程   同时访问哈希映射,以及至少一个线程   从结构上修改地图,必须在外部进行同步。 (一个   结构修改是添加或删除一个或多个的任何操作   更多映射;只是改变与键相关的值   实例已经包含的不是结构修改。)这是   通常通过自然地同步某个对象来完成   封装地图。如果不存在这样的对象,则应该是地图   使用Collections.synchronizedMap方法“包装”。这是最好的   在创建时完成,以防止意外的不同步访问   地图:

 Map m = Collections.synchronizedMap(new HashMap(...));

答案 2 :(得分:2)

volatile 关键字既不会影响 HashMap 上的操作(例如 put get ),也不影响操作 HashMap 中的 ArrayLists volatile 关键字仅影响读取写入对此 HashMap 的特定引用。同样,可以进一步引用相同的 HashMap ,它们不受影响。

如果要同步所有操作: - 参考资料 - HashMap - 和 ArrayList , 然后使用其他 Lock 对象进行同步,如下面的代码所示。

private final Object lock = new Object();
private Map<Object, List<String>> map = new HashMap<>();

// access reference
synchronized (lock) {
    map = new HashMap<>();
}

// access reference and HashMap
synchronized (lock) {
    return map.contains(42);
}

// access reference, HashMap and ArrayList
synchronized (lock) {
    map.get(42).add("foobar");
}

如果未更改引用,则可以使用 HashMap 进行同步(而不是 Lock )。