java - 同步集合/列表的映射

标签 java multithreading collections concurrency synchronization

我想在“Map of Sets”集合上实现一个变体,它将被多个线程不断访问。我想知道我正在进行的同步是否足以保证不会出现任何问题。

所以给出如下代码,其中Map、HashMap和Set是Java实现,Key和Value是一些任意对象:

public class MapOfSets {
    private Map<Key, Set<Value>> map;

    public MapOfLists() {
        map = Collections.synchronizedMap(new HashMap<Key, Set<Value>());
    }

    //adds value to the set mapped to key
    public void add(Key key, Value value) {
        Set<Value> old = map.get(key);

        //if no previous set exists on this key, create it and add value to it
        if(old == null) {
            old = new Set<Value>();
            old.add(value);
            map.put(old);
        }
        //otherwise simply insert the value to the existing set
        else {
            old.add(value);
        }
    }

    //similar to add
    public void remove(Key key, Value value) {...}

    //perform some operation on all elements in the set mapped to key
    public void foo(Key key) {
        Set<Value> set = map.get(key);

        for(Value v : set)
            v.bar();
    }
}

这里的想法是,因为我已经同步了 Map 本身,所以 get() 和 put() 方法应该是原子的,对吧?因此应该不需要对 Map 或其中包含的 Sets 进行额外的同步。那么这行得通吗?

或者,上述代码是否优于另一种可能的同步解决方案:

public class MapOfSets {
    private Map<Key, Set<Value>> map;

    public MapOfLists() {
        map = new HashMap<Key, Set<Value>();
    }

    public synchronized void add(Key key, Value value) {
        Set<Value> old = map.get(key);

        //if no previous set exists on this key, create it and add value to it
        if(old == null) {
            old = new Set<Value>();
            old.add(value);
            map.put(old);
        }
        //otherwise simply insert the value to the existing set
        else {
            old.add(value);
        }
    }

    //similar to add
    public synchronized void remove(Key key, Value value) {...}

    //perform some operation on all elements in the set mapped to key
    public synchronized void foo(Key key) {
        Set<Value> set = map.get(key);

        for(Value v : set)
            v.bar();
    }
}

我让数据结构不同步,而是同步所有可能的公共(public)方法。那么哪些可行,哪些更好?

最佳答案

您发布的第一个实现不是线程安全的。考虑一下当两个具有相同 key 的并发线程访问 add 方法时会发生什么:

  1. 线程 A 执行该方法的第 1 行,并获得一个 null 引用,因为不存在具有给定键的项目
  2. thread B 执行该方法的第 1 行,并获得一个 null 引用,因为不存在具有给定键的项目 — 这将在 A< 之后发生/strong> 从第一次调用返回,因为 map 是同步的
  3. 线程 A 评估 if 条件为 false
  4. 线程 B 评估 if 条件为 false

从那时起,两个线程将继续执行 if 语句的 true 分支,您将丢失两个 value 对象之一。

您发布的方法的第二种变体看起来更安全。


但是,如果你可以使用第三方库,我建议你查看Google Guava ,因为它们提供并发多图 ( docs )。

关于java - 同步集合/列表的映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9553888/

相关文章:

unit-testing - 在 Kotlin 中获取具有不同值的两张 map 的交集

java - 在 JBoss 中使用多个登录模块

java - 模拟退火模板java

java - 将 tryLock() 与 wait() 和 notify()/notifyAll() 一起使用

c - 将函数调用作为参数传递给 printf 和 Reentrancy

multithreading - 在 IIS 下增加 .NET Remoting 应用程序的并发请求

c# - 如果集合尚未通过比较项目的属性将项目添加到集合中?

java - 在 Android WebRTC 应用程序中使用 DataChannel

java - Play Framework 2.3.4 - 如何在 POST 请求上调用 Results.badRequest() 并定向到 Controller 中未通过路由映射的方法?

c# - 检查两个列表是否有冲突元素?