我将Java Map声明为
Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());
处理并发问题,并在地图上同步其上的所有操作。但是,我读到当操作是原子操作时,synchronizedMap
上的同步是不必要的。我检查了Java API,HashMap的文档似乎没有提到哪些是原子的,所以我不确定是哪些。
我正在对以下对地图的调用进行同步:
map.size()
map.put()
map.remove()
map.get()
但如果有些是原子的,那么看起来同步并不是必需的。哪个是原子的?
答案 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
时,性能可能会更差,但是当多个线程同时访问地图时,性能会明显提高。