Java并发性:Volatile vs final in;级联;变量?

Java并发性:Volatile vs final in;级联;变量?,java,concurrency,final,volatile,concurrenthashmap,Java,Concurrency,Final,Volatile,Concurrenthashmap,是 final Map status=new ConcurrentHashMap(); Map statusInner=新的ConcurrentHashMap(); status.put(键,statusInner); 同 final Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>(); Map<Int

final Map status=new ConcurrentHashMap();
Map statusInner=新的ConcurrentHashMap();
status.put(键,statusInner);

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映射状态=新的ConcurrentHashMap();
Map statusInner=新的ConcurrentHashMap();
status.put(键,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);
volatile映射状态=新的ConcurrentHashMap();
volatile Map statusInner=新的ConcurrentHashMap();
status.put(键,statusInner);
如果不是“级联”贴图,final和volatile最终会产生相同的效果,让舒尔的所有线程始终看到贴图的正确内容。。。但是,如果映射本身包含一个映射,会发生什么,如示例中所示。。。我如何使舒尔的内部映射正确地“内存隔离”

坦克!
Tom

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

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);

使用
ConcurrentHashMap
几乎就是您所需要的。是的,如果可以,请参考顶级
映射
最终版
,但在任何情况下都不需要使用
volatile
。内部的第二级
Map
引用是
ConcurrentHashMap
正确处理的业务,我们假设它是正确的。

volatile
只影响其他线程读取它所附加的变量值的能力。它决不会影响另一个线程查看映射的键和值的能力。例如,我可以有一个
volatile int[]
。如果我更改了引用,也就是说,如果我更改了它指向读取数组的其他线程的实际数组,则保证会看到该更改。但是,如果我更改数组的第三个元素,则不会做出这样的保证

如果
status
final
,则包含类的构造会在与任何后续读取的
关系之前创建
,因此它们能够看到status的值。同样地,对
volatile
变量的任何读取都保证看到对它的最新引用赋值。它不像你经常交换真实的地图,更像你只是在改变关键点,而整个地图对象保持原样

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

检索操作(包括get) 通常不堵塞,因此可能会重叠 具有更新操作(包括put) 并移除)。检索反映了 最近完成的调查结果 更新保留在其上的操作 起病

这有点奇怪,但要点是,任何
get
操作如果在某个
put
操作返回后开始,都可以确保看到该put的结果。因此,您甚至不需要在外部地图上使用
volatile
;引用联合联络小组的话:

只能看到引用的线程 在某个对象被删除后,将其添加到该对象 已完全初始化的是 保证正确地看到 为该对象的 最终字段

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

值得一看,特别是它可以让您智能地设置和创建地图。能够设置弱值、启用更好的垃圾收集和过期时间,从而可以使用映射进行有效缓存,这是非常棒的。由于MapMaker制作的映射(:p)具有与ConcurrentHashMap相同的属性,因此您可以对其线程安全性感到满意

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 mapMaker=newmapmaker().weakValues()//为方便起见,请指定
最终地图状态=mapMaker.makeMap();
status.put(key,mapMaker.makeMap());

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

final ConcurrentMap status=new ConcurrentHashMap。。。建议您也可以公开第二级映射必须是并发的,如果您要公开顶级映射而不是封装它们(上面的定义允许将任何映射实现插入顶级;如果您假设第二级的线程安全,则应指定线程安全的映射类型)。“假设这是一个很好的引入bug的方法“:假设一个广泛使用的JDK类的javadoc继承自Doug Lea经过良好战斗测试的并发程序包,其关键是假设的行为,您会认为它可能是一个bug的来源?我没有看到任何javadoc引用、引用或链接。如果您已经记住了
ConcurrentHashMap
的语义,那么一定要记住。我不是说你关于
ConcurrentHashMap
的陈述不正确,或者海报不应该使用它。我是说他/她应该参考Javadocs,确保他/她关于
ConcurrentHashMap
的读/写顺序语义的假设是正确的,特别是如果海报不熟悉这些类。您好jasonmp85,谢谢您的帮助回答。让舒尔仔细检查一下我的理解:您使用int[]阵列的示例非常有趣,也很好。如果我只更改来自不同线程的ArrayElements(而不是数组实例本身),我将被迫“以某种方式同步”对数组元素的访问,以实现“之前发生”关系。(很可能我会在索引上进行同步,如synchronized(index){myintarray[index]=foo;}?我希望这是正确的!非常感谢JLS是这方面的权威来源(),但是是的,您必须强制共享数组元素的两个线程之间进行某种同步,否则一个线程可能看不到另一个线程的写入。一个选项是在任何更新后重新分配数组引用:
volArray[5]=10;volArray=volArray;
。虽然很奇怪,但这种自分配是一种易失性wri