Почему параллельные коллекции Java действительно поточно-ориентированы - PullRequest
8 голосов
/ 23 ноября 2010

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

А как же volatile? Если внутренняя коллекция не является изменчивой, изменения могут быть пропущены другими потоками, и поэтому сохранение потока несколько нарушено. Я знаю, что synchronized может решить эту проблему, но они используют только блокировки без дальнейшей синхронизации.

Это проблема, или я что-то упустил?


Обновление:

После небольшого обсуждения я хочу немного перефразировать вопрос.

Я хочу использовать Java-коллекции в многопоточной среде. (Например, сейчас я говорю о PriorityBlockingQueue)

Я хочу быть уверен, что изменения, внесенные одним потоком в коллекцию (push / pop), сразу видны другим.

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

Вопрос в том, правильно ли я, что одновременные коллекции java не предоставляют эту функцию из коробки? И если я это сделаю, какие дополнительные (минималистичные) методы я должен использовать, чтобы обеспечить желаемую видимость?

Спасибо.

Ответы [ 5 ]

11 голосов
/ 24 ноября 2010

С BlockingQueue Javadoc:

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

PriorityBlockingQueue обеспечивает такое поведение с помощью ReentrantLock, который (являющийся реализацией Lock):

... обеспечивает [s] ту же семантику синхронизации памяти, что и встроенная блокировка монитора, как описано в JLS ...

8 голосов
/ 23 ноября 2010

да, вы что-то упустили. класс ReentrantLock предоставляет те же гарантии, что и синхронизированный. И ReentrantLock, и synchronized предоставляют те же гарантии памяти, что и volatile.

6 голосов
/ 23 ноября 2010

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

Какой источник вы читали?

Это чрезмерное обобщение.Это полностью зависит от того, на какую коллекцию вы смотрите.Например, CopyOnWriteArrayList не делает ничего подобного, но создает абсолютно новый массив каждый раз, когда вы добавляете или удаляете элементы, что означает, что любые открытые Iterator или ListIterator будут продолжать работать со старыми данными;этот эффект преднамеренный.

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

Большинство одновременных коллекций существует, чтобы убедиться, что итераторы продолжают работать на старой версии, а не на новой версиикоторый обновил данные;что-то, что volatile не гарантирует.Такое поведение также означает, что итераторы не останутся в несогласованном состоянии и, следовательно, не допустят выброса ConcurrentModificationException.

5 голосов
/ 23 ноября 2010

РЕДАКТИРОВАТЬ : поскольку первоначальный вопрос был уточнен, этот ответ более не актуален.Приведенный ниже ответ относится к случаю, когда используются Collections.synchronized * методы для обеспечения безопасности многопоточных коллекций.


Если вы синхронизируете блок кода, который также вызывает различныепотоки для синхронизации (возможно, измененного) состояния.

[...] Любое решение требует, чтобы переменная clkID была согласована с основной памятью.Доступ к переменной clkID из синхронизированного метода или блока не позволяет одновременно выполнять этот код, но гарантирует, что переменная clkID в основной памяти обновлена ​​соответствующим образом.Основная память обновляется, когда блокировка объекта получена до выполнения защищенного кода, а затем, когда блокировка снята после выполнения защищенного кода.[...]

Источник : Использовать синхронизированный или изменчивый при доступе к общим переменным

0 голосов
/ 05 декабря 2010

Вот мудрые ответы на ваши вопросы.1. Все структуры данных коллекции Java в пакете параллелизма не переносят соответствующие структуры данных не поточно-безопасного сбора.2. Существуют способы достижения безопасности потоков без использования блокировок или синхронизации.Написание структур данных без блокировок является более сложным процессом, и обычно вам следует избегать его для общего развития.Но если есть один доступный, вы должны использовать их вместо соответствующей версии блокировки структуры данных.3. Блокируйте свободную структуру данных, не используйте блокировки, все же обеспечьте все свойства безопасности и живучести.4. Пример API - ConcurrentSkipListMap, который является параллельной вероятностной картой без блокировки (вероятно, наиболее сложной реализацией в Java)

...