我正在维护一些遗留代码,并在ConcurrentHashMap上找到了一些使用ConcurrentHashMap关键字的实现。对我来说,这似乎没有必要:
public class MyClass{
private final Map<MyObj, Map<String, List<String>>> conMap = new ConcurrentHashMap<>();
//...
//adding new record into conMap:
private void addToMap(MyObj id, String name, String value){
conMap.putIfAbsent(id, new ConcurrentHashMap<>());
Map<String, List<String>> subMap = conMap.get(id);
synchronized(subMap){ // <-- is it necessary?
subMap.putIfAbsent(name, new ArrayList<>());
subMap.get(name).add(value);
}
}
//...
public void doSomthing((MyObj id){
List<Map<String, List<String>>> mapsList = new LinkedList<>();
for(MyObj objId: conMap.keySet()){
if(objId.key1.equals(id.key1)){
mapsList.add(conMap.get(objId));
}
}
for(Map<String, List<String>> map: mapsList){
synchronized(map){ // <-- is it necessary?
if(timeout <= 0){
log(map.size());
for(List<String> value: map.values(){
log(id, value);
}
}
else{
int sum = 0;
for(map.Entry<String, List<String>> val: map.entrySet()){
sum += val.getValue().size();
}
log(sum);
map.wait(timeout);
}
}
//...
}那么,在已经并发的对象上使用synchronized密钥是否合理?或者这是两种不同的东西?
发布于 2019-05-21 12:33:28
在这种情况下:
synchronized(subMap){ // <-- is it necessary?
subMap.putIfAbsent(name, new ArrayList<>());
subMap.get(name).add(value);
}synchronized是必要的。没有它,您可以有两个线程同时更新同一个ArrayList实例。因为ArrayList不是线程安全的,所以addToMap方法也不会是线程安全的.
在这种情况下:
synchronized(map){ // <-- is it necessary?
if(/*condition*/){
log(map.size());
for(List<String> value: map.values(){
log(id, value);
}
}
else{
int sum = 0;
for(map.Entry<String, List<String>> val: map.entrySet()){
sum += val.getValue().size();
}
log(sum);
map.wait(timeout);
}synchronized是必要的。
if分支中,log方法(或从它调用的东西)可能会调用ArrayList::toString,它将迭代每个ArrayList。如果没有子映射级别的同步,则可以由另一个线程同时执行add (例如,addToMap调用)。这意味着存在内存危险,ConcurrentModificationException在toString()方法中可能是可能的。else分支中,size()调用访问子映射中每个ArrayList中的大小字段。如果没有子映射级别的同步,那么其中一个列表上可能会同时出现一个add。这可能导致size()方法返回一个陈旧的值。此外,您不能保证在迭代子映射时看到添加到子映射中的映射项。如果任何一个事件发生,sum可能是不准确的。(这是否真的是一个问题取决于这种方法的要求:不准确的计数是可以接受的。)发布于 2019-05-21 12:27:45
ConcurrentHashMap同步每个单独的方法调用自己,这样没有其他线程能够访问映射(并且可能破坏映射的内部数据结构)。
同步块同步两个或多个连续的方法调用,因此没有其他线程可以修改调用之间的数据结构(并且可能破坏数据的一致性,涉及应用程序逻辑)。
注意,只有在使用同一个监视器对象从同步块执行对HashMap的所有访问时,同步块才能工作。
发布于 2019-05-21 12:31:34
这在某种程度上是必要的,因为多个线程可能试图同时附加到同一个ArrayList。synchonized正在防止这种情况发生,因为ArrayList显然是不同步的。
由于Java 8,我们有computeIfAbsent,这意味着它们正在执行的puts和gets后面的操作可以简化。我会这样写,不需要同步:
conMap.computeIfAbsent(id, k -> new ConcurrentHashMap<>())
.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>()) // or other thread-safe list
.add(value);https://stackoverflow.com/questions/56238241
复制相似问题