Java同步HashMap中的size(),put(),remove(),get()是否为原子?

时间:2014-07-02 20:38:20

标签: java multithreading hashmap synchronization atomic

我将Java Map声明为

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

处理并发问题,并在地图上同步其上的所有操作。但是,我读到当操作是原子操作时,synchronizedMap上的同步是不必要的。我检查了Java API,HashMap的文档似乎没有提到哪些是原子的,所以我不确定是哪些。

我正在对以下对地图的调用进行同步:

map.size()

map.put()

map.remove()

map.get()

但如果有些是原子的,那么看起来同步并不是必需的。哪个是原子的?

3 个答案:

答案 0 :(得分:11)

顾名思义的同步地图已同步。对其上的任何其他操作而言,其上的每个操作都是原子的。

您可以将其视为同步地图的每个方法都使用synchronized关键字声明。

请记住,虽然单个操作是原子操作,但如果将它们组合起来,它们就不再是原子操作,例如:

String value = map.get("key");
map.put("key", value+"2");

不等同于您的自定义同步代码:

synchronized (map) {
    String value = map.get("key");
    map.put("key", value+"2");
}

而是:

synchronized (map) {
    String value = map.get("key");
}
synchronized (map) {
    map.put("key", value+"2");
}

答案 1 :(得分:5)

不保证HashMap具有原子操作。从不同的线程(甚至size())调用它的任何方法可能会破坏地图。但是,使用Collections.synchronizedMap获得的地图将使每个调用同步(因此是线程安全的)。

但是,您可能需要更高级别的同步。例如,如果您测试是否存在密钥,读取大小,或以其他方式从地图访问某些内容,然后根据结果对地图执行其他操作,则地图可能在两次调用之间发生了更改。在这种情况下,您需要一个synchronized块来使整个事务成为原子,而不是同步映射(只使每个调用成为原子)。

答案 2 :(得分:4)

地图本身是同步的,而不是一些内部锁。在地图上运行多个操作需要同步块。无论如何,如果您使用的是JDK 1.6或更高版本,则应考虑使用ConcurrentHashMap

当您需要确保数据一致性时,

ConcurrentHashMap是最佳的,并且每个线程都需要当前的地图视图。如果性能至关重要,并且每个线程仅将数据插入到地图中,并且读取的频率较低,则使用您已概述的路径。也就是说,当一个线程一次只访问ConcurrentHashMap时,性能可能会更差,但是当多个线程同时访问地图时,性能会明显提高。