Java中同步Hashmap与ConcurrentHashMap的性能评估

时间:2015-11-25 00:27:54

标签: java collections hashmap

我已根据此https://dzone.com/articles/java-7-hashmap-vs编写了一个小测试来测试哪种方法表现更好

threadSafeMap2 = new HashMap<String, Integer>(2);
threadSafeMap2 = Collections.synchronizedMap(threadSafeMap2);

threadSafeMap3 = new ConcurrentHashMap<String, Integer>(2)

这是我的junit测试:

package com.bm.framework.concurrent;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

class WorkerThread implements Runnable 
{

    static final int WORKER_ITERATION_COUNT = 500000;
    private Map<String, Integer> map = null;

    public WorkerThread(Map<String, Integer> assignedMap) 
    {

          this.map = assignedMap;
    }

    @Override
    public void run() 
    {
          for (int i=0; i < WORKER_ITERATION_COUNT; i++) 
          {

                 // Return 2 integers between 1-1000000 inclusive

                 Integer newInteger1 = (int) Math.ceil(Math.random() * 1000000);

                 Integer newInteger2 = (int) Math.ceil(Math.random() * 1000000);                    

                 // 1. Attempt to retrieve a random Integer element

                 Integer retrievedInteger = map.get(String.valueOf(newInteger1));

                 // 2. Attempt to insert a random Integer element

                 map.put(String.valueOf(newInteger2), newInteger2);                
          }
    }
}



public class BmfThreadPoolTest 
{
    private static final int NB_THREADS = 3;
    private static final int NB_TEST_ITERATIONS = 50;


    private static Map<String, Integer> nonThreadSafeMap = null;
    private static Map<String, Integer> threadSafeMap2 = null;
    private static Map<String, Integer> threadSafeMap3 = null;


    @Test
    public void testMapPerformance() throws InterruptedException 
    {
        // this is a test between two styles we see
        // one is to use Collections.synchronizedMap()
        // and the other one is to use directly ConcurrentHashMap which 
        // one is faster?
        // Plain old HashMap (since JDK 1.2)
        nonThreadSafeMap = new HashMap<String, Integer>(2);

        // Fully synchronized HashMap
        threadSafeMap2 = new HashMap<String, Integer>(2);
        threadSafeMap2 = Collections.synchronizedMap(threadSafeMap2);

        // ConcurrentHashMap (since JDK 1.5)
        threadSafeMap3 = new ConcurrentHashMap<String, Integer>(2);
        System.out.println("ConcurrentHashMap");
        beginTest(threadSafeMap3);
        // the second one is always performing poor no matter whether it is hashmap or concurrenthashmap why????
        System.out.println("Collections.synchronizedMap()");
        beginTest(threadSafeMap2);

    }

    private void beginTest(final Map<String, Integer> assignedMapForTest)
    {
        for (int i=0; i<NB_TEST_ITERATIONS; i++) 
        {
            long timeBefore = System.currentTimeMillis();
            long timeAfter = 0;

            Float totalProcessingTime = null;

            ExecutorService executor = Executors.newFixedThreadPool(NB_THREADS);

            for (int j = 0; j < NB_THREADS; j++) 
            {

                   /** Assign the Map at your convenience **/

                   Runnable worker = new WorkerThread(assignedMapForTest);
                   executor.execute(worker);              

            }

            // This will make the executor accept no new threads

            // and finish all existing threads in the queue

            executor.shutdown();

            // Wait until all threads are finish

            while (!executor.isTerminated()) 
            {

            }

            timeAfter = System.currentTimeMillis();

            totalProcessingTime = new Float( (float) (timeAfter - timeBefore) / (float) 1000);

            System.out.println("All threads completed in "+totalProcessingTime+" seconds");
        }
    }   
}

问题出在两个beginTest()调用之间,第二个调用总是表现不好,即如果我这样运行

beginTest(threadSafeMap3);
beginTest(threadSafeMap2); 

最后一个需要更长的时间来完成指向ConcurrentHashMap更快的事实。如果我像这样交换订单

beginTest(threadSafeMap2);
beginTest(threadSafeMap3); 

最后一个需要更长的时间来完成指向ConcurrentHashMap更慢的事实。 为什么我会根据测试中使用地图的顺序得到相互矛盾的结果?

如果我注释掉其中一个并在两个单独的运行中运行测试(一个用于同步哈希映射,一个用于ConcurrentHashMap),那么我总是以ConcurrentHashMap作为获胜者获得一致的结果。

2 个答案:

答案 0 :(得分:2)

  1. 您应该使用您的实际使用模式对此进行测试:与您的程序的其余部分相比,地图的开销很可能微不足道 - 即使不是这样,地图的使用方式也很重要。因此,测量整个应用程序通常比单个微量基准测试组件更好。
  2. 在微基准测试中,您应该预热测试中的代码,因此在定时阶段不会首先采用代码路径,以确保您不会对解释代码或即时编译本身进行基准测试
  3. 您应该将测试分成不同的方法。这将允许垃圾收集器释放第一次测试运行所消耗的内存,因此两个测试都以相同的堆大小和垃圾收集开销运行。 (在您的代码中,JVM可能没有意识到第二次测试开始后不再使用第一张地图......)
  4. 背景阅读:How do I write a correct micro-benchmark in Java?

答案 1 :(得分:-1)