Время выполнения ConcurrentHaspMap и HashTable

В литературе вы можете узнать, что ConcurrentHashMap намного быстрее, чем HashTable. Мне просто интересно, почему следующий код этого не показывает.

В следующем коде некоторые числа заполняются как в ConcurrentHashMap (chm), так и в HashTable (ht). Есть два эквивалентных Callable (один для chm и другой для ht), которые выполняют одну и ту же операцию над элементами chm или ht и возвращают время выполнения в мс.

У меня время выполнения ht в два раза меньше, чем у chm.

public class ConcurrentHashMapDemo {

    static ConcurrentHashMap<Long, Double> chm;
    static Hashtable<Long, Double> ht;
    int result1;
    int result2;
    static long numbers = 1000000;

    public ConcurrentHashMapDemo(){
        chm = new ConcurrentHashMap<>();
        ht = new Hashtable<>();
        result1=0;
        result2=0;
    }
    // just do some operation with map or table elements
    private class Th1 implements Callable<Integer>{
        public Integer call(){
            long base = System.currentTimeMillis();
            for(Entry<Long, Double> e: chm.entrySet()){
                result1+=e.getKey();
                result1+=(int)(e.getValue()*10);
            }
            long time = System.currentTimeMillis();
            return (int)(time-base);
        }
    }

    private class Th2 implements Callable<Integer>{
        public Integer call(){
            long base = System.currentTimeMillis();
            for(Entry<Long, Double> e: ht.entrySet()){
                result2+=e.getKey();
                result2+=(int)(e.getValue()*10);
            }
            long time = System.currentTimeMillis();
            return (int)(time-base);
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        ConcurrentHashMapDemo chmd = new ConcurrentHashMapDemo();
        for(long i=0;i<numbers;i++){
            chm.put((Long)i, Math.sqrt(i));
            ht.put((Long)i, Math.sqrt(i));
        }
        ExecutorService ex = Executors.newCachedThreadPool();
        try {
            Future<Integer>s11 = ex.submit(chmd.new Th1());
            System.out.println("chm "+s11.get());   // appr. 220
            Thread.sleep(1000);
            Future<Integer>s21 = ex.submit(chmd.new Th2());
            System.out.println("ht "+s21.get());    // appr. 110
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        ex.shutdown();
    }
}

person pronyn    schedule 13.11.2014    source источник


Ответы (1)


Вы сравниваете яблоки с апельсинами здесь.

ConcurrentHashMap является параллельным — это означает, что вы можете использовать его в большем количестве потоков одновременно.

Хэш-таблица синхронизирована (синхронизированы некоторые методы работы с ней - получить/поместить/очистить...) -> операции с ней взаимоисключающие. Будьте осторожны, итератор не является потокобезопасным, и поэтому он может (а не может - видимость памяти) генерировать ConcurrentModificationException, если вы пытаетесь изменить его одновременно при повторении в каком-то другом потоке.

Вы сравниваете производительность итерации по картам в одном потоке (каждая карта будет работать в одном потоке). Тогда Hashtable быстрее, потому что его реализация намного проще (он не является потокобезопасным, не поддерживает параллельные модификации и т. д.).

Если бы вы создали другой тест с одновременной модификацией/доступом к картам, вы бы обнаружили, что ConcurrentHashMap работает быстрее (потому что он является параллельным :-), а Hashtable — нет).

person jakub.petr    schedule 13.11.2014