Почему Java Synchronized Collections не использует блокировки чтения/записи? - программирование
Подтвердить что ты не робот

Почему Java Synchronized Collections не использует блокировки чтения/записи?

После таких вещей, как Hashtable и Vector, где не рекомендуется, и когда собраны синхронизированные обертки коллекции, я подумал, что синхронизация будет обрабатываться более эффективно. Теперь, когда я просмотрел код, я удивлен, что на самом деле это просто упаковка коллекций с помощью блоков синхронизации.

Почему ReadWriteLock не входит в состав, например, SynchronizedMap в коллекциях? Есть ли соображения эффективности, которые не делают это того стоит?

4b9b3361

Ответ 1

Блокировки чтения и записи являются частью оптимизации производительности, что означает, что она может позволить более высокий concurrency в определенных ситуациях. Необходимым условием является то, что они применяются к структурам данных, которые читаются большую часть времени, но не изменены.

В других условиях они выполняют несколько хуже, чем эксклюзивные блокировки, что естественно, поскольку они имеют большую сложность.

Это наиболее эффективно, если блокировки блокировки чтения и записи обычно хранятся в течение умеренно длительного времени и только несколько изменений в охраняемых ресурсах.

Следовательно, ли блокировки чтения и записи лучше, чем эксклюзивные блокировки, зависит от варианта использования. В конце концов вам нужно измерить с профилированием, какие блокировки работают лучше.

Учитывая это, кажется целесообразным выбрать эксклюзивную блокировку для Collections.synchronizedMap , обращаясь к общему прецеденту, а не к специальному случаю с большей частью читателей.

Дополнительные ссылки

enter image description here

[...] Однако в конфигурации, где операции записи были больше распространенная версия с синхронизированными блоками была на 50% быстрее, чем один на основе блокировок чтения и записи с JVM Sun 1.6.0_07 (на 14% быстрее с Sun 1.5.0_15 JVM).

В случае с низким уровнем конкуренции с 1 читатель и 1 писатель, различия в производительности были менее экстремальными, и каждый из трех типов замков дал самую быструю версию на по меньшей мере одна конфигурация машины /VM (например, обратите внимание, что ReentrantLocks был самым быстрым на двухъядерной машине с Sun 1.5.0_15 JVM).

Ответ 2

Я не думаю, что использование ReadWriteLock (если это то, о чем вы говорите) происходит быстрее, чем использование ключевого слова synchronized. Обе конструкции накладывают блокировки и возводят барьеры памяти и "случаются до".

Возможно, вы говорите о том, чтобы делать что-то умное в Collections.synchronizedMap(...) и друзьях, где методы чтения считываются как заблокированные, а методы записи записываются в блокировку для производительности. Это может отлично работать с классами коллекции java.util, но может вызвать проблемы синхронизации с Map, реализованные пользователями, если подсчитываются методы get() или что-то в этом роде - то есть, когда метод, который был "только для чтения", фактически делал обновления к коллекции. Да, делать это было бы ужасной идеей.

ConcurrentHashmap был написан с высокой производительностью и использует поля volatile непосредственно вместо блоков synchronized. Это значительно усложняет код по сравнению с Collections.synchronizedMap(...), но и быстрее. Именно по этой причине рекомендуется для высокопроизводительных ситуаций более Collections.synchronizedMap(new HashMap<...>()).

Ответ 3

Большинство аргументов были рассмотрены, кроме следующих. SynchronizedMap/Set/List, а также Hashtable и Vector полагаются на синхронизацию экземпляра коллекции. В результате многие разработчики использовали эту синхронизацию для обеспечения атомарности. Например.

List syncList = Collections.synchronizedList(new ArrayList());

//put if absent
synchronized(syncList){
   if(!syncList.contains(someObject)){
     syncList.add(someObject);
   }
}

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

Таким образом, использование ReadWriteLock для этих коллекций лишится возможности для атомных операций, если только вы не сможете захватить блокировки.

Ответ 4

Это связано с тем, что эти классы предшествовали блокированию чтения/записи, которые довольно рано появились в Java (Java 5). Во всяком случае, они редко бывают полезны из-за их жестко закрепленной мелкозернистой фиксации.

Ответ 5

Ничего об эффективности. Нет ничего плохого в использовании synchronized в Collections.synchronizedMap. Если ReadWriteLock используется для реализации Карты, я бы назвал ее Collections.LockedMap;)

Будьте серьезны, Collections.synchronizedMap был написан за несколько лет до появления Lock. Это API, который не может быть изменен после выпуска.

Ответ 6

Чтобы добавить к @JohnVint answer, рассмотрите проблему итерации. документация явно требует синхронизации с клиентом:

Обязательно, чтобы пользователь вручную синхронизировал список при его повторении:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

Несоблюдение этого совета может привести к недетерминированному поведению.

Это работает только потому, что клиент может совместно использовать встроенную блокировку на возвращенной карте. Если бы он использовал внутреннюю блокировку чтения/записи, возвращаемый интерфейс должен был бы поддерживать некоторый способ доступа, по крайней мере, к блокировке чтения для безопасной итерации. Это будет усложнять API для сомнительной выгоды (как объяснили другие).