java 8 Map.merge 是线程安全的吗?

标签 java java-8

import java.util.HashMap;
import java.util.Map;

public class MainTest
{
    static Map<String, Integer> map = new HashMap();

    public static void incr()
    {
        map.merge("counter", 1, Integer::sum);
    }

    public static void decr()
    {
        map.merge("counter", -1, Integer::sum);
    }

    public static void main(String[] args) throws Exception
    {
        map.put("counter", 0);
        for (int i = 0; i < 10000; i++)
        {
            Thread t1 = new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    incr();
                }
            });
            t1.join();
            t1.start();

            Thread t2 = new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    decr();
                }
            });
            t2.join();
            t2.start();
        }
      System.out.println(map);
    }

}

当 main 方法运行时,结果是 {counter=-2}。 为什么不是0?

最佳答案

Map 接口(interface)上merge 的Javadoc 说:

The default implementation makes no guarantees about synchronization or atomicity properties of this method. Any implementation providing atomicity guarantees must override this method and document its concurrency properties.

虽然 HashMap 覆盖了默认实现,但它没有关于该实现的并发属性的文档,但它确实有以下一般声明:

Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally

因此它不是线程安全的。

P.S.,不清楚为什么在启动相应线程之前调用 t1.join()t2.join()

如果你撤销调用

    t1.join();
    t1.start();

    t1.start();
    t1.join();

    t2.join();
    t2.start();

    t2.start();
    t2.join();

你会得到 0 的输出。当然,如果你这样做,根本不会有并发修改,因为每个线程都会在前一个线程结束后启动。

另一种方法是在外部同步 map.merge 调用:

public static void incr()
{
    synchronized(map) {map.merge("counter", 1, Integer::sum);}
}

public static void decr()
{
    synchronized(map) {map.merge("counter", -1, Integer::sum);}
}

关于java 8 Map.merge 是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47647664/

相关文章:

java - “every class in java extends the MetaClass Object” 是否意味着每个子类都会导致 Diamond 问题

java - 空指针异常和 Swing

java - 如何收集DoubleStream到List

java - 在内部将一个窗口添加到另一个窗口

java - 为什么 JAXB 2 注册机的 XJC 简单模式会更改集合名称?

java - xpath 2.0 for java 可能

java - 正确终止java流

java - 在 Win10 上切换 java 版本 - 注册 key 问题

lambda - 带有空值检查的 Java 8 lambda 比较器

java - 如何一次获取 500 行,直到获取表中的所有项目?