簡體   English   中英

在性能方面,ConcurrentHashMap和Collections.synchronizedMap(Map)之間有什么區別?

[英]What's the difference between ConcurrentHashMap and Collections.synchronizedMap(Map) in term of performance?

我試圖通過使用代碼來評估這些概念。 這就是我最終的結果

    public void runCWith3Threads() {

        // mesure add with 3 threads
        for (int i = 0; i < 10; i++) {
            Map<Integer, Person> shm = Collections.synchronizedMap(new HashMap<Integer, Person>());
            Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>();

            MapThread sm1 = new MapThread(shm, 0, 20000, "sm1");
            MapThread sm2 = new MapThread(shm, 20000, 30000, "sm2");
            MapThread sm3 = new MapThread(shm, 30000, 50000, "sm3");

            sm1.start();sm2.start();sm3.start();

            while (true) {
                try {
                    sm1.join();
                    sm2.join();
                    sm3.join();
                    break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            long secondMax = sm1.time > sm2.time ? sm1.time : sm2.time;
            long firstMax = secondMax > sm3.time ? secondMax : sm3.time;

            System.out.println("Millisec of SynchronizedMap cost: " + firstMax);

            MapThread m1 = new MapThread(chm, 0, 20000, "m1");
            MapThread m2 = new MapThread(chm, 20000, 30000, "m2");
            MapThread m3 = new MapThread(chm, 30000, 50000, "m3");

            m1.start();m2.start();m3.start();

            while (true) {
                try {
                    m1.join();
                    m2.join();
                    m3.join();
                    break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            secondMax = m1.time > m2.time ? m1.time : m2.time;
            firstMax = secondMax > m3.time ? secondMax : m3.time;

            System.out.println("Millisec of ConcurrentHashMap cost: " + firstMax);
            System.out.println();
        }
    }

    public class MapThread extends Thread {
        Map<Integer, Person> map;
        int from;
        int to;
        long time;
        String name;

        public MapThread(Map<Integer, Person> map, int from, int to, String name) {
            this.map = map;
            this.from = from;
            this.to = to;
            this.name = name;
        }

        public void run() {

            long start = System.currentTimeMillis();
            for (int i = from; i < to; i++) {
                map.put(i, new Person());
            }
            long end = System.currentTimeMillis();
            time = end - start;
            return;
        }
    }

我期望的是,在代碼運行之后, ConcurrentHashMap的結果會更快,因為它允許多次插入到地圖中。 對於SynchronizedMap ,由於每個線程都在等待前一個線程完成(映射已同步),因此一旦運行單個線程環境,代碼就會起到相同作用

但是,結果並沒有完全反映出我的預期

Millisec of SynchronizedMap cost: 250
Millisec of ConcurrentHashMap cost: 203

Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 172

Millisec of SynchronizedMap cost: 172
Millisec of ConcurrentHashMap cost: 188

Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 172

Millisec of SynchronizedMap cost: 187
Millisec of ConcurrentHashMap cost: 172

Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 189

Millisec of SynchronizedMap cost: 187
Millisec of ConcurrentHashMap cost: 171

Millisec of SynchronizedMap cost: 188
Millisec of ConcurrentHashMap cost: 171

Millisec of SynchronizedMap cost: 172
Millisec of ConcurrentHashMap cost: 172

Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 188

這是為什么 ?

更新

Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>(100000, 10, 3);

我有結果

Millisec of SynchronizedMap cost: 208
Millisec of ConcurrentHashMap cost: 216

Millisec of SynchronizedMap cost: 255
Millisec of ConcurrentHashMap cost: 196

Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>(100000);

我有結果

Millisec of SynchronizedMap cost: 204
Millisec of ConcurrentHashMap cost: 283

Millisec of SynchronizedMap cost: 203
Millisec of ConcurrentHashMap cost: 200

如果您正在進行基准測試,您應該:

  1. 做一個預熱階段(由於JIT編譯和類加載),
  2. 多次重復測試(由於垃圾收集)。

如果我創建類似於您的基准測試的基准測試,我會得到以下結果:

Warmup...
Benchmark...
 4 *  500000: 0.22s / 0.04s
 4 * 1000000: 0.55s / 0.10s
 4 * 1500000: 1.10s / 0.16s
 4 * 2000000: 0.90s / 0.19s
 4 * 2500000: 1.68s / 0.25s

第一個數字表示線程數,第二個數字表示int范圍的大小,第三個數字表示同步Map的持續時間,第四個數字表示ConcurrentHashMap的持續時間。 如您所見, ConcurrentHashMap在所有情況下都明顯更快。

您可以在下面找到整個Java代碼。 請注意,它使用了Java 8中的功能。但是,這應該對結果沒有影響:

public static void main(String... args) {
    System.out.println("Warmup...");
    for (int i = 0; i < 10000; ++i) {
        test(Collections.synchronizedMap(new HashMap<>()), 2, 1000);
        test(new ConcurrentHashMap<>(), 2, 1000);
    }
    System.out.println("Benchmark...");
    for (int i = 0; i < 5; ++i) {
        int threads = 4;
        int range = 500000 * (i + 1);
        System.out.printf("%2d * %7d: %s / %s\n",
            threads, range,
            test(Collections.synchronizedMap(new HashMap<>()), threads, range),
            test(new ConcurrentHashMap<>(), threads, range));
    }
}
public static String test(Map<Integer,Object> map, int threads, int range) {
    long duration = IntStream.range(0, 10)
        .mapToLong(i -> execute(
                IntStream.range(0, threads)
                .<Runnable>mapToObj(t -> () -> bulkPut(map, t * range, (t + 1) * range, new Object()))
                .toArray(Runnable[]::new)))
        .min().getAsLong();
    return String.format("%4.2fs",
        duration / 1000.0, threads, range);
}
public static <T> void bulkPut(Map<Integer,T> map, int from, int to, T value) {
    for (int i = from; i < to; ++i) {
        map.put(i, value);
    }
}
public static long execute(Runnable... runnables) {
    List<Thread> threads = new ArrayList<>();
    AtomicLong duration = new AtomicLong();
    for (Runnable runnable : runnables) {
        Thread thread = new Thread(() -> {
                long start = System.currentTimeMillis();
                try {
                    runnable.run();
                } finally {
                    long elapsed = System.currentTimeMillis() - start;
                    duration.accumulateAndGet(elapsed, Math::max);
                }
            });
        thread.start();
        threads.add(thread);
    }
    for (Thread thread : threads) {
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    return duration.get();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM