首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >同步ConcurrentHashMap

同步ConcurrentHashMap
EN

Stack Overflow用户
提问于 2019-05-21 12:24:23
回答 4查看 223关注 0票数 0

我正在维护一些遗留代码,并在ConcurrentHashMap上找到了一些使用ConcurrentHashMap关键字的实现。对我来说,这似乎没有必要:

代码语言:javascript
复制
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密钥是否合理?或者这是两种不同的东西?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2019-05-21 12:33:28

在这种情况下:

代码语言:javascript
复制
    synchronized(subMap){                            // <-- is it necessary?
        subMap.putIfAbsent(name, new ArrayList<>());
        subMap.get(name).add(value);
    }

synchronized是必要的。没有它,您可以有两个线程同时更新同一个ArrayList实例。因为ArrayList不是线程安全的,所以addToMap方法也不会是线程安全的.

在这种情况下:

代码语言:javascript
复制
        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调用)。这意味着存在内存危险,ConcurrentModificationExceptiontoString()方法中可能是可能的。
  • else分支中,size()调用访问子映射中每个ArrayList中的大小字段。如果没有子映射级别的同步,那么其中一个列表上可能会同时出现一个add。这可能导致size()方法返回一个陈旧的值。此外,您不能保证在迭代子映射时看到添加到子映射中的映射项。如果任何一个事件发生,sum可能是不准确的。(这是否真的是一个问题取决于这种方法的要求:不准确的计数是可以接受的。)
票数 2
EN

Stack Overflow用户

发布于 2019-05-21 12:27:45

ConcurrentHashMap同步每个单独的方法调用自己,这样没有其他线程能够访问映射(并且可能破坏映射的内部数据结构)。

同步块同步两个或多个连续的方法调用,因此没有其他线程可以修改调用之间的数据结构(并且可能破坏数据的一致性,涉及应用程序逻辑)。

注意,只有在使用同一个监视器对象从同步块执行对HashMap的所有访问时,同步块才能工作。

票数 2
EN

Stack Overflow用户

发布于 2019-05-21 12:31:34

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

由于Java 8,我们有computeIfAbsent,这意味着它们正在执行的puts和gets后面的操作可以简化。我会这样写,不需要同步:

代码语言:javascript
复制
conMap.computeIfAbsent(id, k -> new ConcurrentHashMap<>())
    .computeIfAbsent(name, k -> new CopyOnWriteArrayList<>()) // or other thread-safe list
    .add(value);
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56238241

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档