Подтвердить что ты не робот

Итерация по потоку по коллекции

Мы все знаем при использовании Collections.synchronizedXXX (например, synchronizedSet()), мы получаем синхронизированный "вид" базовой коллекции.

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

Какой вариант вы решите решить эту проблему?

Я могу видеть только следующие подходы:

  • Сделайте это, как указано в документации: синхронизация в коллекции
  • Клонирование коллекции перед вызовом iterator()
  • Используйте коллекцию, в которой итератор потокобезопасен (я знаю только CopyOnWriteArrayList/Set)

И в качестве бонусного вопроса: при использовании синхронизированного представления - это использование foreach/Iterable thread-safe?

4b9b3361

Ответ 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.