Java 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

我正在维护一些遗留代码,并在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);
            }
    }

    //...

}

所以,在已经并发的对象上使用同步密钥合理吗?或者这是两件不同的事情?

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访问列表,那么您必须跨越同步障碍。如果超时