Java ConcurrentHashMap与同步
我正在维护一些遗留代码,并在ConcurrentHashMap上找到了一些带有同步关键字的实现。我觉得没有必要:Java ConcurrentHashMap与同步,java,concurrency,Java,Concurrency,我正在维护一些遗留代码,并在ConcurrentHashMap上找到了一些带有同步关键字的实现。我觉得没有必要: public class MyClass{ private final Map<MyObj, Map<String, List<String>>> conMap = new ConcurrentHashMap<>(); //... //adding new record into conMap: p
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);
}
}
//...
}
所以,在已经并发的对象上使用同步密钥合理吗?或者这是两件不同的事情?ConcurrentHashMap会同步每个单独的方法调用本身,这样其他线程就无法访问映射并可能破坏映射的内部数据结构 Synchronized block同步两个或多个连续的方法调用,这样就没有其他线程可以修改调用之间的数据结构,并可能破坏数据在应用程序逻辑方面的一致性
请注意,synchornized块仅在使用同一监视器对象从同步块执行对HashMap的所有访问时才起作用。这有点必要,因为多个线程可能会同时尝试附加到同一ArrayList。synchonized正在防止这种情况发生,因为ArrayList显然没有同步 由于Java 8,我们有ComputeFabSent,这意味着可以简化Put和Get后面的操作。我会这样写,不需要同步:
conMap.computeIfAbsent(id, k -> new ConcurrentHashMap<>())
.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>()) // or other thread-safe list
.add(value);
在这种情况下:
synchronized(subMap){ // <-- is it necessary?
subMap.putIfAbsent(name, new ArrayList<>());
subMap.get(name).add(value);
}
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);
}
同步是必要的
在if分支中,log方法或从中调用的东西可能会调用ArrayList::toString,它将迭代每个ArrayList。如果不在submap级别进行同步,则可能会有另一个线程同时进行添加,例如addToMap调用。这意味着存在内存危害,toString方法中可能存在ConcurrentModificationException
在else分支中,size调用访问submap中每个ArrayList中的size字段。如果不在submap级别进行同步,则可能会在其中一个列表上同时添加一个。这可能会导致size方法返回过时的值。此外,在迭代submap时,不能保证看到添加到submap的映射条目。如果这两个事件中的任何一个发生,总和可能不准确。这是否真的是一个问题取决于该方法的要求:不准确的计数是可以接受的
其他答案不足以说明这一点
for(Map<String, List<String>> map: mapsList){
synchronized(map){ // <-- is it necessary?
if(/*condition*/){
...iterate over map...
}
else {
...iterate over map...
}
}
}
有必要吗?很难说
什么是/*条件*/?在映射上同步是否会阻止其他线程A在线程B测试/*condition*/之后,但在线程B执行这两个分支之前或期间更改其值?如果是这样,那么同步块可能非常重要
那些迭代怎么样?在映射上同步是否会阻止其他线程A在线程B迭代时更改映射的内容?如果是这样,那么同步块可能非常重要。@Lino啊,我错过了。按照今天的标准,Java8代码实际上是遗留的;谢谢你的回答。computeIfAbsent听起来很有趣,需要检查它,但synchronized在subMap上,它如何保护ArrayList?@Eli如果同步对subMap的访问,并且-至关重要的是-您只通过subMap访问列表,那么您必须跨越同步障碍。如果超时