首页 > 解决方案 > 带同步的 ConcurrentHashMap

问题描述

我正在维护一些遗留代码,并找到了一些带有synchronized关键字 on 的实现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那么,在已经并发的对象上使用 key 是否合理?或者这是两个不同的东西?

标签: javaconcurrency

解决方案


这是有必要的,因为多个线程可能会尝试同时附加到同一个线程ArrayListsynchonized正在防止这种情况发生,因为显然ArrayList不同步。

从 Java 8 开始,我们有了computeIfAbsent这意味着他们正在执行的 put 和 get 可以被简化。我会这样写,不需要同步:

conMap.computeIfAbsent(id, k -> new ConcurrentHashMap<>())
    .computeIfAbsent(name, k -> new CopyOnWriteArrayList<>()) // or other thread-safe list
    .add(value);

推荐阅读