使用ConcurrentHashMap,我發現computeIfAbsent比putIfAbsent慢兩倍。在這裏,簡單的測試:爲什麼ConcurrentHashMap :: putIfAbsent比ConcurrentHashMap :: computeIfAbsent更快?
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())執行時間是5009毫秒(當然,它有所不同,但主要趨勢是這樣的)。最有趣的是testCase3 - 它與testCase1非常相似(除了使用putIfAbsent()代替computeIfAbsent()),但其執行速度快兩倍(testCase3爲5010ms,而testCase1爲10068ms)。看看源代碼,對於computeIfAbsent()和putVal()(在putIfAbsent()中使用它)幾乎是相同的。
有誰知道是什麼導致了線程執行時間的不同嗎?
你沒有測量任何東西。使用JMH做適當的微基準測試 –
您需要在測試之前設置代碼。每次運行多次測試至少10秒鐘,並忽略這些結果(只計算後面的結果) –
'putIfAbsent()'已經有對象。 'computeIfAbsent()'必須執行一個方法來確定對象。這全部記錄在案。 – EJP