不可修改的地图是否需要同步?

时间:2009-06-12 16:34:24

标签: java concurrency

我正在使用JDK 1.4 ...所以我无法访问1.5 +中的好并发内容。

考虑以下类片段:

private Map map = Collections.EMPTY_MAP;

public Map getMap() {
    return map;
}

public synchronized void updateMap(Object key, Object value) {
    Map newMap = new HashMap(map);
    newMap.put(key, value);
    map = Collections.unmodifiableMap(newMap);
}

是否有必要同步(或挥发)地图引用,因为我将只允许通过updateMap方法更新地图(已同步)?

将在多个线程中访问(读取)地图对象,尤其是通过迭代器。知道迭代器会在后端地图的结构发生变化时抛出异常,我想我会使地图不可修改。因此,当我通过updateMap更改地图的结构时,现有的迭代器将继续处理“旧”地图(这对我来说很好)。

副作用是,我不必担心同步读取。在本质上,与写入相比,我将具有更大的读取幅度。当前正在迭代地图对象的线程将继续这样做,任何启动的新线程都将获取最新的地图。 (好吧,我假设它会在这里考虑erickson的评论 - Java concurrency scenario -- do I need synchronization or not?

有人可以评论这个想法是否合适吗?

谢谢!

3 个答案:

答案 0 :(得分:6)

应该使用volatile关键字,以确保线程会看到最新的Map版本。否则,如果没有同步,则没有保证其他线程将看到除空地图之外的任何内容。

由于updateMap()已同步,因此对其的每次访问都会看到map的最新值。因此,您不会丢失任何更新。这是有保证的。但是,由于getMap()未同步且map不是volatile,因此无法保证线程会看到map的最新值,除非该线程本身是最新的线程来更新地图。使用volatile将解决此问题。

但是,您执行可以访问Java 1.5和1.6并发添加项。存在backport。我强烈建议使用backport,因为当您能够迁移到更高版本的JDK时,它将允许轻松迁移到JDK并发类,并且它允许比您的方法更高的性能。 (虽然如果您map的更新很少,那么您的表现应该没问题。)

答案 1 :(得分:1)

从技术上讲,如果使用volatile map,则不需要同步updateMap(更新'map'是原子的,并且该方法中的所有其他指令都在当前线程的本地对象上运行)。

答案 2 :(得分:0)

我知道这不是你问题的一部分。但是,如果您担心并发性并且不使用Java 1.5 for ConcurrentHashMap。使用Hashtable的不可变实例。它是一个阻塞并发映射实现,它处理所有并发写入和读取。