简体   繁体   English

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

[英]Java Concurrency : Volatile vs final in “cascaded” variables?

is

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

the same as 同样的

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

in case the inner Map is accessed by different Threads? 如果内部Map由不同的线程访问?

or is even something like this required: 或者甚至是这样的要求:

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

In case the it is NOT a "cascaded" map, final and volatile have in the end the same effect of making shure that all threads see always the correct contents of the Map... But what happens if the Map iteself contains a map, as in the example... How do I make shure that the inner Map is correctly "Memory barriered"? 如果它不是一个“级联”地图,那么final和volatile最终会产生同样的效果,即所有线程总是看到Map的正确内容...但是如果Map iteself包含一个地图会发生什么,在示例中...如何使内部地图正确“内存瘫痪”?

Tanks! 坦克! Tom 汤姆

volatile only affects the ability of other threads to read the value of the variables it's attached to. volatile仅影响其他线程读取其附加变量值的能力。 It in no way affects the ability of another thread to see the keys and values of the map. 它决不会影响另一个线程查看地图的键和值的能力。 For instance, I could have a volatile int[] . 例如,我可以有一个volatile int[] If I change the reference—ie if I change the actual array that it points to—other threads reading the array are guaranteed to see that change. 如果我更改引用 - 即如果我更改它指向的实际数组 - 其他线程读取数组保证看到该更改。 However, if I change the third element of the array no such guarantees are made. 但是,如果我更改数组的第三个元素,则不会进行此类保证。

If status is final , the construction of the containing class creates a happens-before relationship with any subsequent reads, so they are able to see the value of status. 如果statusfinal ,则包含类的构造会创建与任何后续读取happens-before关系,因此它们可以查看status的值。 Likewise any reads to your volatile variable are guaranteed to see the latest reference assignment to it. 同样,对volatile变量的任何读取都保证会看到对它的最新引用赋值。 It's unlike you're swapping the actual map around very often, more like you're just changing keys and the overall map object stays as is. 这与你经常交换实际地图不同,更像是你只是改变键,整个地图对象保持不变。

For this question, then, we need to consult the documentation for ConcurrentHashMap : 对于这个问题,我们需要查阅ConcurrentHashMap的文档:

Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). 检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。 Retrievals reflect the results of the most recently completed update operations holding upon their onset. 检索反映了最近完成的更新操作的结果。

This is kind of oddly worded, but the gist is that any get operation whose onset is after some put operation's return is guaranteed to see the results of that put. 这有点奇怪的措辞,但要点是任何get操作,其开始是一些put操作的返回之后保证看到put的结果。 So you don't even need a volatile on the outer map; 所以你甚至不需要在外图上使用volatile ; quoth the JLS: JLS:

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields. 在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值。

Summary 摘要

A final on the outer map is sufficient. 外部地图上的final就足够了。

It's worth looking at Google-Collections and, in particular, MapMaker that lets you intelligently setup and create Maps. 值得一看的是Google-Collections ,特别是MapMaker ,它可以让您智能地设置和创建地图。 Being able to setup weak values, to enable better garbage collection, and expiration times, so you can use Maps for effective caching, is brilliant. 能够设置弱值,实现更好的垃圾收集和到期时间,因此您可以使用Maps进行有效的缓存,非常棒。 As the Maps that MapMaker makes (:p) have the same properties as ConcurrentHashMap, you can be happy with its thread-safety. 由于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());

Please note, you might want to look at your definition of statusInner, as it doesn't seem right. 请注意,您可能希望查看statusInner的定义,因为它似乎不正确。

I think the best answer here is that volatile is not a way to ensure thread-safety. 我认为这里最好的答案是volatile不是确保线程安全的方法。

Using ConcurrentHashMap is pretty much all you need. 使用ConcurrentHashMap几乎就是您所需要的。 Yes, make the reference to the top-level Map final if you can, but volatile is not necessary in any event. 是的,如果可以,请参考顶级Map final ,但在任何情况下都不需要volatile The second-level Map reference inside are the business of ConcurrentHashMap to get right, and one assumes it does. 内部的第二级Map引用是ConcurrentHashMap的业务,为了正确,并且假设它确实如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM