Итерация по потоку по коллекции
Мы все знаем при использовании Collections.synchronizedXXX
(например, synchronizedSet()
), мы получаем синхронизированный "вид" базовой коллекции.
Однако в документе этих методов генерации оболочки указано, что мы должны явно синхронизировать с коллекцией при итерации коллекций с использованием итератора.
Какой вариант вы решите решить эту проблему?
Я могу видеть только следующие подходы:
- Сделайте это, как указано в документации: синхронизация в коллекции
- Клонирование коллекции перед вызовом
iterator()
- Используйте коллекцию, в которой итератор потокобезопасен (я знаю только
CopyOnWriteArrayList
/Set)
И в качестве бонусного вопроса: при использовании синхронизированного представления - это использование foreach/Iterable thread-safe?
Ответы
Ответ 1
Вы уже ответили на свой бонусный вопрос: нет, использование цикла с расширенным циклом небезопасно - потому что он использует итератор.
Что касается наиболее подходящего подхода - это действительно зависит от того, как ваш контекст:
- Пишут очень редко? Если это так,
CopyOnWriteArrayList
может быть наиболее подходящим.
- Является ли коллекция разумно малой, а итерация быстрой? (т.е. вы не выполняете много работы в цикле). Если это так, синхронизация может быть в порядке - особенно если это происходит не так часто (т.е. у вас не будет большого разногласия по поводу коллекции).
- Если вы много работаете и не хотите блокировать другие потоки, работающие в одно и то же время, хит клонирования коллекции может быть приемлемым.
Ответ 2
Зависит от вашей модели доступа. Если у вас низкий concurrency и частые записи, у 1 будет лучшая производительность. Если у вас высокий concurrency с и нечастая запись, у 3 будет лучшая производительность. Вариант 2 будет работать плохо во всех случаях.
foreach
вызывает iterator()
, поэтому применяются одинаковые вещи.
Ответ 3
Вы можете использовать один из новых коллекций, добавленных в Java 5.0, которые поддерживают одновременный доступ во время итерации. Другой подход - взять копию, используя toArray, которая является потокобезопасной (во время копирования).
Collection<String> words = ...
// enhanced for loop over an array.
for(String word: words.toArray(new String[0])) {
}
Ответ 4
Я мог бы полностью отказаться от ваших требований, но если вы не знаете о них, просмотрите google-collections с помощью "Факультативной неизменности" в виду.
Ответ 5
Я предлагаю сбросить Collections.synchronizedXXX
и обрабатывать все блокировки равномерно в клиентском коде. Базовые коллекции не поддерживают тип составных операций, полезных в потоковом коде, и даже если вы используете java.util.concurrent.*
, код сложнее. Я предлагаю сохранить как можно больше кода нить-агностик. Сохраняйте сложный и подверженный ошибкам потокобезопасный (если нам повезет) код до минимума.
Ответ 6
Все три варианта будут работать. Выбор правильного для вашей ситуации будет зависеть от вашей ситуации.
CopyOnWriteArrayList
будет работать, если вам нужна реализация списка, и вы не возражаете, чтобы при копировании записывалось основное хранилище. Это довольно хорошо для производительности, если у вас нет очень больших коллекций.
ConcurrentHashMap
или "ConcurrentHashSet
" (используя Collections.newSetFromMap
) будет работать, если вам нужен интерфейс Map
или Set
, очевидно, что вы не получаете случайного доступа таким образом. Один большой! Дело в том, что они будут хорошо работать с большими наборами данных - при мутировании они просто копируют небольшие бит базового хранилища данных.
Ответ 7
Это зависит от результата, которому нужно достичь клонирования/копирования/toArray(), нового ArrayList (..), и подобные получают моментальный снимок и не блокируют сбор.
Использование синхронизированной (сбор) и итерации, обеспечивающей к концу итерации, не будет изменяться, то есть эффективно блокировать ее.
сторона примечания: (toArray() обычно предпочтительнее с некоторыми исключениями, когда внутренне необходимо создать временный ArrayList). Также обратите внимание, что ничего, кроме toArray(), также должно быть обернуто в синхронизацию (сбор), предоставляемое с помощью Collections.synchronizedXXX.