Java同步HashMap中的size()、put()、remove()、get()是原子的吗?

Java同步HashMap中的size()、put()、remove()、get()是原子的吗?,java,multithreading,hashmap,synchronization,atomic,Java,Multithreading,Hashmap,Synchronization,Atomic,我将Java映射声明为 Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>()); 但是,如果其中一些是原子的,那么这些似乎不需要同步。哪个是原子的?正如名称所示,同步映射是同步的。它上的每个操作相对于它上的任何其他操作都是原子的 您可以将其视为使用synchronized关键字声明同步映射的每个方法 请记住,尽管单个操作是原子操作,但如果将它们组合起来,它们

我将Java映射声明为

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

但是,如果其中一些是原子的,那么这些似乎不需要同步。哪个是原子的?

正如名称所示,同步映射是同步的。它上的每个操作相对于它上的任何其他操作都是原子的

您可以将其视为使用
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");
}

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


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

映射本身是同步的,而不是一些内部锁。在映射上运行多个操作确实需要同步块。在任何情况下,如果使用JDK 1.6或更大,则应考虑使用


ConcurrentHashMap
在需要确保数据一致性,并且每个线程都需要地图的当前视图时是最佳选择。如果性能非常关键,并且每个线程只向映射中插入数据,而读取的频率较低,则使用您概述的路径。也就是说,一次只有一个线程访问一个
ConcurrentHashMap
时,性能可能会更差,但当多个线程同时访问映射时,性能会显著提高。

HashMap
不是一个同步的集合,因此您应该假设没有操作是原子的。所有方法都是原子的,但多个方法调用不是。我想这与另一篇文章的区别在于,提出问题的另一个人希望同步多个方法。还要注意的是,只有通过同步包装器对映射进行任何访问时,操作才会正确同步。如果直接调用内部HashMap的任何方法(传递给
集合的方法。synchronizedMap
),它不仅不再是原子的,而且可能导致未定义的行为。@Marwin幸运的是,OP没有保存对内部映射的引用,尽管即使他愿意,我也认为直接使用它会导致未定义的行为。只有通过内部映射和同步包装器发布对映射的两个并发访问时,才会出现这种情况。@ciamej感谢您的澄清。这就是我的意思,但没有明确地说。当我们谈论操作的原子性时,我不知怎的认为存在一些并发访问。也许我用错了词。Maurice Herlihy和Nir Shavit在他们的书《多处理器编程的艺术》中使用了“合成”一词。他们说,“当系统中的每个对象满足P时,系统作为一个整体满足P,那么正确性属性P就是合成的。”我每次方法调用只调用一个映射函数,每个map函数调用独立于前面的函数调用。“所以我是清白的,对吧?”La comadreja-从你的描述来看,你应该是清白的。但这完全取决于是否需要将方法调用序列作为一个单元来处理。如果是这样的话,那么对于那些需要使用
同步的
块而不是(或除了)同步映射函数的块。感谢您深思熟虑的回答。映射中的每个方法都是一个不同的单元,而不是属于一个连续单元的多个方法。
synchronized (map) {
    String value = map.get("key");
}
synchronized (map) {
    map.put("key", value+"2");
}