【问题标题】:Synchronized collection vs synchronized method?同步收集与同步方法?
【发布时间】:2015-09-01 07:57:17
【问题描述】:

我有一个类容器,其中包含一个将被多个线程使用的集合:

public class Container{

    private Map<String, String> map;

    //ctor, other methods reading the map

    public void doSomeWithMap(String key, String value){
        //do some threads safe action
        map.put(key, value);
        //do something else, also thread safe
    }
}

有什么更好的办法,声明方法synchronized

public synchronized void doSomeWithMap(String key, String value)

还是使用标准的线程安全装饰器?

Collections.synchronizedMap(map);

【问题讨论】:

标签: java multithreading collections


【解决方案1】:

一般来说,同步地图将保护大多数访问它,而无需进一步考虑。但是,“同步地图”对于迭代来说是不安全的,这可能是一个问题,具体取决于您的用例。 It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views.

考虑使用ConcurrentHashMap 是否符合您的用例。

如果此对象有其他状态需要保护免受并发错误,那么您将需要使用同步或Lock

【讨论】:

  • 那么,synxhronizedMap 之间的主要区别在于读取的访问权限不同步,对吧?因此,与 concurrentHashMap 相比,它给了我们性能开销...
  • @St.Antario ConcurrentHashMap 对哈希箱组使用单独的锁,我相信默认情况下为 16。这通常意味着更少的锁争用,因为实现可以计算没有争用的键的散列,然后只锁定映射的 1/16。 ConcurrentHashMap 还允许线程安全的迭代。
【解决方案2】:

如果您的doSomeWithMap 方法将多次访问地图,您必须同步doSomeWithMap 方法。如果only 访问是显示的put() 调用,那么最好使用ConcurrentHashMap

请注意,“多次”是任何调用,迭代器本质上是多次“获取”。

【讨论】:

    【解决方案3】:

    如果您查看SynchronizedMap 的实现,您会发现它只是一个包装了一个在调用任何方法之前使用互斥锁的非线程安全映射的映射

    public V get(Object key) {
      synchronized (mutex) {return m.get(key);}
    }
    
    public V put(K key, V value) {
      synchronized (mutex) {return m.put(key, value);}
    }
    
    public Set<Map.Entry<K,V>> entrySet() {
      synchronized (mutex) {
        if (entrySet==null)
          entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
        return entrySet;
      }
    }
    

    如果您只想保护getput,这个实现可以帮您完成。

    但是,如果您想要一个可以由两个或多个线程迭代和更新的 Map,则不适合,在这种情况下,您应该使用 ConcurrentHashMap

    【讨论】:

      【解决方案4】:

      如果您在doSomeWithMap 中执行的其他操作会在不同线程同时执行时导致问题(例如,它们更新类级变量),那么您应该同步整个方法。如果不是这种情况,那么您应该使用同步的Map 以最小化同步锁保留的时间长度。

      【讨论】:

        【解决方案5】:

        您可能应该让块根据要求进行同步。请注意这一点。

        当您使用 ConcurrentHashMap 之类的同步 Collection 或 synchronizedMap()synchronizedList() 等 Collection 的方法时,只有 Map/List 是同步的。为了进一步解释,

        考虑一下,

        Map<String, Object> map = new HashMap<>();
        Map<String, Object> synchMap = Collections.synchronizedMap(map);
        

        这使得地图的get操作是同步的,而不是其中的对象。

        Object o = synchMap.get("1");// Object referenced by o is not synchronized. Only the map is.
        

        如果你想保护 Map 中的对象,那么你还必须将代码放在同步块中。记住这一点很好,因为在大多数情况下,许多人忘记保护对象。

        也可以看看这个Collection's synchronizedMap

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-06
          • 1970-01-01
          • 1970-01-01
          • 2014-04-18
          相关资源
          最近更新 更多