简体   繁体   English

ConcurrentHashMap与同步

[英]ConcurrentHashMap with synchronized

I am maintaining some legacy code and found some implementation with synchronized key-word on ConcurrentHashMap . 我正在维护一些旧代码,并在ConcurrentHashMap上找到了一些使用synchronized关键字的实现。 It seem unnecessary to me: 对我来说似乎没有必要:

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);
            }
    }

    //...

}

So, is it reasonable to use synchronized key on object that already concurrent? 那么,在已经并发的对象上使用synchronized密钥是否合理? Or those are two different things? 还是那是两个不同的东西?

ConcurrentHashMap synchronizes each individual method call itself, so that no other thread can access the map (and possibly break the internal data structure of the map). ConcurrentHashMap同步每个单独的方法调用本身,以便其他线程无法访问该映射(并可能破坏该映射的内部数据结构)。

Synchronized block synchronizes two or more consecutive method calls, so that no other thread can modify the data structure between the calls (and possibly break the consistency of the data, with regards to the application logic). 同步块可以同步两个或多个连续的方法调用,以便其他线程无法修改调用之间的数据结构(就应用程序逻辑而言,可能会破坏数据的一致性)。

Note that the synchornized block only works if all access to the HashMap is performed from synchronized blocks using the same monitor object. 请注意,仅当使用相同的监视对象从同步块执行对HashMap的所有访问时,synchornized块才起作用。

It is sort of necessary, as multiple threads may try to append to the same ArrayList at the same time. 这是必要的,因为多个线程可能会尝试同时附加到同一ArrayList The synchonized is protecting against that happening as ArrayList is obviously not synchronized. synchonized是一种防止这种情况发生的ArrayList显然是不同步的。

Since Java 8 we have computeIfAbsent which means the puts followed by gets they are doing can be simplified. 从Java 8开始,我们有了computeIfAbsent ,这意味着可以简化执行操作的puts和gets。 I would write it like this, no synchronization required: 我会这样写,不需要同步:

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

In this case: 在这种情况下:

    synchronized(subMap){                            // <-- is it necessary?
        subMap.putIfAbsent(name, new ArrayList<>());
        subMap.get(name).add(value);
    }

the synchronized is necessary. synchronized是必要的。 Without it, you could have two threads simultaneously updating the same ArrayList instance. 没有它,您可能会有两个线程同时更新同一ArrayList实例。 Since ArrayList is not thread-safe, the addToMap method would not be thread-safe either. 由于ArrayList不是线程安全的,因此addToMap方法也不是线程安全的。

In this case: 在这种情况下:

        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);
        }

the synchronized is necessary. synchronized是必要的。

  • In the if branch, the log method (or something called from it) will probably call ArrayList::toString which will iterate each ArrayList . if分支中, log方法(或从中调用的方法)可能会调用ArrayList::toString ,它将迭代每个ArrayList Without the synchronizing at the submap level, there could be a simultaneous add by another thread (eg an addToMap call). 如果没有在子图级别进行同步,则另一个线程可能会同时进行add (例如, addToMap调用)。 That means that there are memory hazards, and a ConcurrentModificationException may be possible in the toString() method. 这意味着存在内存危险,并且toString()方法中可能会出现ConcurrentModificationException

  • In the else branch, the size() call is accessing a size field in each ArrayList in the submap. else分支中, size()调用正在访问子映射中每个ArrayList中的size字段。 Without the synchronizing at the submap level, there could be a simultaneous add on one of those list. 如果不在子图级别进行同步,则可能会在这些列表之一上同时add That could cause the size() method to return a stale value. 这可能导致size()方法返回陈旧的值。 In addition, you are not guaranteed to see map entries added to a submap while your are iterating it. 此外,在迭代时,不能保证您会看到添加到子地图的地图条目。 If either of those events happen, the sum could be inaccurate. 如果这些事件之一发生,则sum可能不准确。 (Whether that is really an issue depends on the requirements for this method: inaccurate counts could be acceptable.) (无论这是一个真正的问题取决于该方法的要求:计数错误可以接受的)

Other answers don't adequately this bit... 其他答案还不够充分...

   for(Map<String, List<String>> map: mapsList){
        synchronized(map){                       // <-- is it necessary?
            if(/*condition*/){
                ...iterate over map...
            }
            else {
                ...iterate over map...
            }
        }
   }

Is it necessary? 有必要吗? Hard to tell. 很难说。

What is /*condition*/ ? 什么是/*condition*/ Does synchronizing on map prevent some other thread A from changing the value of /*condition*/ after thread B has tested it, but before or while thread B is performing either of the two branches? 在线程B测试之后,但在线程B执行两个分支中的任何一个之前或同时,在map同步是否会阻止其他线程A更改/*condition*/的值? If so, then the synchronized block could be very important. 如果是这样,则synchronized块可能非常重要。

How about those iterations? 那些迭代怎么样? Does synchronizing on map prevent some other thread A from changing the contents of the map while thread B is iterating? 是否在同步map防止一些其他的线程A从改变地图的内容,但线程B迭代? If so, then the synchronized block could be very important. 如果是这样,则synchronized块可能非常重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM