Java并发:“级联”变量中的易失性与最终性?

时间:2010-06-03 08:06:19

标签: java concurrency final volatile concurrenthashmap

final Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

相同
volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer,   Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

如果内部Map由不同的线程访问?

或甚至是这样的要求:

volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
volatile Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

如果它不是“级联”地图,那么final和volatile最终会产生同样的效果,即所有线程总是看到Map的正确内容...但是如果Map iteself包含会发生什么一个地图,如示例中所示......我如何确保内部地图正确“记忆bar”?

坦克! 汤姆

3 个答案:

答案 0 :(得分:9)

volatile仅影响其他线程读取其附加变量值的能力。它决不会影响另一个线程查看地图的键和值的能力。例如,我可以有一个volatile int[]。如果我改变参考 - 即。如果我更改它指向的实际数组 - 其他线程读取数组保证看到该更改。但是,如果我更改数组的第三个元素,则不会做出这样的保证。

如果statusfinal,则包含类的构造会与任何后续读取创建happens-before关系,因此他们可以看到状态值。同样,对volatile变量的任何读取都可以保证看到最新的参考分配。这与你经常交换实际地图不同,更像是你只是改变键,整个地图对象保持不变。

对于这个问题,我们需要查阅ConcurrentHashMap的文档:

  

检索操作(包括get)   一般不阻塞,所以可能重叠   更新操作(包括put   并删除)。检索反映了   最近完成的结果   更新操作持有他们的   发作。

这有点奇怪的措辞,但要点是任何get操作,其某些put操作返回后的保证看到该放置的结果。所以你甚至不需要外部地图上的volatile; JLS:

  

只能看到引用的线程   在该对象之后的对象   已经完全初始化了   保证看到正确   该对象的初始化值   最后的领域。

摘要

外部地图上的final就足够了。

答案 1 :(得分:4)

值得一看Google-Collections,尤其是MapMaker,可让您智能地设置和创建地图。能够设置弱值,实现更好的垃圾收集和到期时间,因此您可以使用Maps进行有效的缓存,非常棒。由于MapMaker使得(:p)具有与ConcurrentHashMap相同的属性,因此您可以对其线程安全感到满意。

final mapMaker = new MapMaker().weakValues(); //for convenience, assign
final Map<Integer,Map<String,Integer>> status = mapMaker.makeMap();
status.put(key, mapMaker.<String, Integer>makeMap());

请注意,您可能需要查看statusInner的定义,因为它似乎不正确。

答案 2 :(得分:1)

我认为这里最好的答案是volatile不是确保线程安全的方法。

使用ConcurrentHashMap几乎就是您所需要的。是的,如果可以,请引用顶级Map final,但无论如何都不需要volatile。内部的第二级Map引用是ConcurrentHashMap正确的业务,并且假设它正确。