Почему Java Synchronized Collections не использует блокировки чтения/записи?
После таких вещей, как Hashtable
и Vector
, где не рекомендуется, и когда собраны синхронизированные обертки коллекции, я подумал, что синхронизация будет обрабатываться более эффективно. Теперь, когда я просмотрел код, я удивлен, что на самом деле это просто упаковка коллекций с помощью блоков синхронизации.
Почему ReadWriteLock
не входит в состав, например, SynchronizedMap в коллекциях? Есть ли соображения эффективности, которые не делают это того стоит?
Ответы
Ответ 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 для сомнительной выгоды (как объяснили другие).