ConcurrentHashMap computeIfAbsent

前端 未结 3 1207
灰色年华
灰色年华 2020-12-09 11:27

There is a new computeIfAbsent API introduced in Java 8. The javadocs for ConcurrentHashMap\'s impelementation of it state:

If the specified key is no

3条回答
  •  孤城傲影
    2020-12-09 11:35

    You can get contention when the value already exists.

    If you look at the source code for computeIfAbsent(), it's pretty complex but you see that the check whether the value is already present is inside the synchronized block. Consider this alternate version (which doesn't operate atomically):

    /**
     * Alternate implementation that doesn't block when map already
     * contains the value
     */
    public V computeIfAbsent2(K key, Function mappingFunction) {
        V value = get(key);
        if (value == null) {
            value = mappingFunction.apply(key);
            put(key, value);
        }
        return value;
    }
    

    I ran a JMH test comparing this alternate implementation with the original. I ran 20 threads, and used a ConcurrentHashMap containing 20 values that already existed. Each thread would use all 20 values. The test exercised only the case that the value already exists. It ran on OS X. The result (after a 2-minute warmup) was

    Benchmark                                     Mode  Cnt       Score   Error   Units
    ComputIfAbsentTest.benchComputeAbsent        thrpt    2   77966.354          ops/ms
    ComputIfAbsentTest.benchComputeAbsent2       thrpt    2  463096.033          ops/ms
    

    I also tried running this with Flight Recording enabled, and the contention was clearly visible. Here's an example stack trace:

    "local.ComputIfAbsentTest.benchComputeAbsent-jmh-worker-11" #25 daemon prio=5 os_prio=31 tid=0x00007f89da10b000 nid=0x7203 waiting for monitor entry [0x00007000021f8000]
       java.lang.Thread.State: BLOCKED (on object monitor)
        at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1674)
        - waiting to lock <0x0000000795f80540> (a java.util.concurrent.ConcurrentHashMap$Node)
        at local.ComputIfAbsentTest.benchComputeAbsent(ComputIfAbsentTest.java:87)
        at local.generated.ComputIfAbsentTest_benchComputeAbsent_jmhTest.benchComputeAbsent_thrpt_jmhStub(ComputIfAbsentTest_benchComputeAbsent_jmhTest.java:116)
        at local.generated.ComputIfAbsentTest_benchComputeAbsent_jmhTest.benchComputeAbsent_Throughput(ComputIfAbsentTest_benchComputeAbsent_jmhTest.java:76)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:430)
        at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:412)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    

提交回复
热议问题