java - 为什么 ConcurrentHashMap::putIfAbsent 比 ConcurrentHashMap::computeIfAbsent 快?

标签 java performance

使用 ConcurrentHashMap,我发现 computeIfAbsent 比 putIfAbsent 慢两倍。这里是简单的测试:

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;


public class Test {
    public static void main(String[] args) throws Exception {
        String[] keys = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a0", "a01", "a02", "a03", "a04", "a05", "a06", "a07", "a08", "a09", "a00"};

        System.out.println("Test case 1");
        long time = System.currentTimeMillis();
        testCase1(keys);
        System.out.println("ExecutionTime: " + String.valueOf(System.currentTimeMillis() - time));

        System.out.println("Test case 2");
        time = System.currentTimeMillis();
        testCase2(keys);
        System.out.println("ExecutionTime: " + String.valueOf(System.currentTimeMillis() - time));

        System.out.println("Test case 3");
        time = System.currentTimeMillis();
        testCase3(keys);
        System.out.println("ExecutionTime: " + String.valueOf(System.currentTimeMillis() - time));
    }

    public static void testCase1(String[] keys) throws InterruptedException {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

        List<Thread> threads = new ArrayList<>();

        for (String key : keys) {
            Thread thread = new Thread(() -> map.computeIfAbsent(key, s -> {
                System.out.println(key);
                String result = new TestRun().compute();
                System.out.println("Computing finished for " + key);
                return result;
            }));
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }

    public static void testCase2(String[] keys) throws InterruptedException {
        List<Thread> threads = new ArrayList<>();

        for (String key : keys) {
            Thread thread = new Thread(() -> {
                System.out.println(key);
                new TestRun().compute();
                System.out.println("Computing finished for " + key);
            });
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }


    public static void testCase3(String[] keys) throws InterruptedException {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

        List<Thread> threads = new ArrayList<>();

        for (String key : keys) {
            Thread thread = new Thread(() -> {
                Callable<String> c = () -> {
                    System.out.println(key);
                    String result = new TestRun().compute();
                    System.out.println("Computing finished for " + key);
                    return result;
                };

                try {
                    map.putIfAbsent(key, c.call());
                } catch (Exception e) {
                    e.printStackTrace(System.out);
                }
            });
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }

}

class TestRun {
    public String compute() {
        try {
            Thread.currentThread().sleep(5000);
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
        return UUID.randomUUID().toString();
    }
}

在我的笔记本电脑上运行这个测试,testCase1(使用 computeIfAbsent())的执行时间是 10068ms,testCase2(执行相同的东西但没有将其包装到 computeIfAbsent())的执行时间是 5009ms(当然它会有所不同有点,但主要趋势就是这样)。最有趣的是 testCase3 - 它与 testCase1 几乎相同(除了使用 putIfAbsent() 而不是 computeIfAbsent()),但它的执行速度快了两倍(testCase3 为 5010ms,testCase1 为 10068ms)。

查看源代码,computeIfAbsent() 和 putVal()(在底层的 putIfAbsent() 中使用)几乎相同。

有人知道是什么导致线程执行时间不同吗?

最佳答案

您遇到记录的功能:

Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.

computeIfAbsent 检查键是否存在并锁定 map 的某些部分。然后它调用仿函数并将结果放入映射(如果返回值不为空)。只有在那之后这部分 map 才被解锁。

另一方面,test3总是调用c.call(),计算结束后调用putIfAbsent。

关于java - 为什么 ConcurrentHashMap::putIfAbsent 比 ConcurrentHashMap::computeIfAbsent 快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40081456/

相关文章:

java - 使用 Java 发送电子邮件,连接到 gmail 主机挂起

java - 如何检查一个字符串是否可以使用另一个字符串中的字符进行拼写?

Ruby 数组 find_first 对象?

Java replaceAll 因源字符串中的美元符号而失败

javascript - 在 Google Chrome 的隐身窗口中打开链接

Java登录系统方法

c++ - 将数据从一个线程传递到另一个线程的最快方法

c - 从磁盘读取文件期间使用中断

Java增加还是减少?绩效评估

条件评估的Python性能