Java ConcurrentHashMap лучше, чем производительность HashMap? - PullRequest
22 голосов
/ 14 июля 2011

Я только что прочитал книгу «Чистый код» и наткнулся на это утверждение:

Когда я был молод, Даг Ли написал основную книгу [8] «Параллельное программирование на Java».Наряду с книгой он разработал несколько поточно-ориентированных коллекций, которые позже стали частью JDK в пакете java.util.concurrent.Коллекции в этом пакете безопасны для многопоточных ситуаций и хорошо работают. Фактически, реализация ConcurrentHashMap работает лучше, чем HashMap, в почти во всех ситуациях.Он также допускает одновременное одновременное чтение и запись и имеет методы, поддерживающие общие составные операции, которые в противном случае не являются поточно-ориентированными.Если Java 5 является средой развертывания, начните с ConcurrentHashMap

Обратите внимание, что в приведенной выше цитате я использовал «[n]», где n - некоторое число, чтобы указать места, в которых автор предоставилссылки, и, как вы можете видеть, он не предоставил никаких ссылок на жирную часть.

Не то, чтобы я не верил этому утверждению, но я хотел бы знать подтверждающие доказательства этого утверждения.Итак, кто-нибудь знает какие-либо ресурсы, которые показывают статистику производительности для ConcurrentHashMap и HashMap?Или кто-нибудь может объяснить мне, почему ConcurrentHashMap быстрее, чем HashMap?

Я, вероятно, посмотрю реализацию ConcurrentHashMap на работе, когда у меня перерыв, но сейчас я хотел бы услышать ответы от других SO.

Ответы [ 4 ]

11 голосов
/ 14 июля 2011

Если вы обращаетесь к HashMap только с одним потоком, HashMap является самым быстрым (он не выполняет никакой синхронизации), если вы обращаетесь к нему из нескольких потоков, ConcurrentHashMap работает быстрее, чем грубая синхронизация вручную. Смотрите здесь для небольшого сравнения:

http://www.codercorp.com/blog/java/why-concurrenthashmap-is-better-than-hashtable-and-just-as-good-hashmap.html

10 голосов
/ 15 июля 2011

Даг Ли чрезвычайно хорош в этих вещах, поэтому я не удивлюсь, если в свое время его ConcurrentyHashMap работает лучше, чем HashMap Джошуа Блоха.Однако, начиная с Java 7, первым автором HashMap стал и Дуг Ли.Очевидно, что теперь нет причин, по которым HashMap будет работать медленнее, чем его двоюродный брат.

Из любопытства я все равно провел некоторый тест.Я запускаю его под Java 7. Чем больше записей, тем ближе производительность.В конечном счете ConcurrentHashMap находится в пределах 3% от HashMap, что весьма примечательно.Узким местом в действительности является доступ к памяти, как говорится, «память - это новый диск (а диск - это новая лента)».Если записи находятся в кеше, оба будут быстрыми;если записи не помещаются в кеш, оба будут медленными.В реальных приложениях карта не должна быть большой, чтобы конкурировать с другими за то, что они находятся в кеше.Если карта используется часто, она кэшируется;если нет, то он не кэшируется, и это реальный определяющий фактор, а не реализации (учитывая, что оба они реализованы одним и тем же экспертом)

public static void main(String[] args)
{
    for(int i=0; i<100; i++)
    {
        System.out.println();

        int entries = i*100*1000;
        long t0=test( entries, new FakeMap() );
        long t1=test( entries, new HashMap() );
        long t2=test( entries, new ConcurrentHashMap() );

        long diff = (t2-t1)*100/(t1-t0);
        System.out.printf("entries=%,d time diff= %d%% %n", entries, diff);
    }
}


static long test(int ENTRIES, Map map)
{
    long SEED = 0;
    Random random = new Random(SEED);

    int RW_RATIO = 10;

    long t0 = System.nanoTime();

    for(int i=0; i<ENTRIES; i++)
        map.put( random.nextInt(), random.nextInt() );

    for(int i=0; i<RW_RATIO; i++)
    {
        random.setSeed(SEED);
        for(int j=0; j<ENTRIES; j++)
        {
            map.get( random.nextInt() );
            random.nextInt();
        }
    }
    long t = System.nanoTime()-t0;
    System.out.printf("%,d ns %s %n", t, map.getClass());
    return t;
}


static class FakeMap implements Map
{
    public Object get(Object key)
    {
        return null;  
    }
    public Object put(Object key, Object value)
    {
        return null;  
    }
    // etc. etc.
}
7 голосов
/ 19 апреля 2013

Причина, по которой HashMap может быть медленнее, заключается в том, что он должен обнаруживать ConcurrentModification, чтобы знать, когда генерировать исключение. ConcurrentHashMap не должен проверять modCount, чтобы знать, когда бросать (но он использует его для size () и isEmpty ()). Получение блокировки происходит очень быстро, особенно в однопоточных ситуациях, когда вы уже удерживаете блокировку, но проверка modCount - это два чтения и переход, если не равен, что HashMap должен заплатить, чтобы вызвать CoModException.

Я рекомендую прочитать исходные классы ваших коллекций, чтобы вы знали, сколько работы они выполняют, когда выполняете вызов метода. В ситуациях, когда у вас есть полностью приватная карта только для получения / размещения словаря, вы часто можете использовать урезанный HashMap без какого-либо modCount или даже отслеживания размера для дополнительного повышения производительности.

6 голосов
/ 14 июля 2011

Это своего рода резкое утверждение, которое трудно доказать тем или иным способом.Как вы измеряете что-то в «почти во всех ситуациях» ?

A ConcurrentHashMap, вероятно, будет лучше, чем синхронизированный HashMap.Чем больше разногласий, тем значительнее будет разница.С другой стороны, несинхронизированный HashMap, вероятно, будет быстрее, чем ConcurrentHashMap, из-за накладных расходов на ненужную блокировку в последнем случае.


Я также хотел бы увидеть контекстдля этого утверждения, и какие доказательства автор книги выдвигает в поддержку этого.И доказательство неустановленного предположения о том, что «почти все» сценарии использования для хэш-карт включают синхронизацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...