Как клонировать синхронизированную коллекцию? - PullRequest
6 голосов
/ 03 января 2011

Представьте себе синхронизированный Collection:

Set s = Collections.synchronizedSet(new HashSet())

Какой лучший способ клонировать эту коллекцию?

Желательно, чтобы клонирование не требовалосьсинхронизация в исходной коллекции, но требуется, чтобы итерация клонированной коллекции не требовала синхронизации в исходной коллекции.

Ответы [ 3 ]

7 голосов
/ 03 января 2011

Используйте конструктор копирования внутри синхронизированного блока:

synchronized (s) {
    Set newSet = new HashSet(s); //preferably use generics
}

Если вам также необходимо синхронизировать копию, снова используйте Collections.synchronizedSet(..).

Согласно комментарию Питера - вам нужно будет сделать это в синхронизированном блоке на исходном наборе. Документация synchronizedSet явно об этом:

Крайне важно, чтобы пользователь вручную синхронизировал возвращаемый набор при его итерации

3 голосов
/ 03 января 2011

При использовании синхронизированных наборов, имейте в виду, что вы будете подвергаться синхронизации при доступе к каждому элементу в наборе. Collections.synchronizedSet() просто оборачивает ваш набор оболочкой, которая заставляет синхронизировать каждый метод. Вероятно, не то, что вы действительно хотели. A ConcurrentSkipListSet даст вам лучшую производительность в многопоточной среде, где несколько потоков будут записывать в набор.

ConcurrentSkipListSet позволит вам выполнить следующее:

Set newSet = s.clone();//preferably use generics

Нередко используется клон набора для обработки снимков. Если это то, что вам нужно, вы можете добавить небольшой код для обработки случая, когда элемент уже обработан. Накладные расходы, связанные со случайным объектом, включенным в более чем один набор копий, обычно меньше согласованных накладных расходов при использовании Collections.concurrentSet().

РЕДАКТИРОВАТЬ: Я только что заметил, что ConcurrentSkipListSet является Cloneable и обеспечивает потокобезопасный метод clone(). Я изменил свой ответ, потому что я действительно считаю, что это лучший вариант - вместо потери масштабируемости и производительности до Collections.concurrentSet().

1 голос
/ 03 января 2011

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

Set newSet = new HashSet(Arrays.asList(s.toArray())); 

РЕДАКТИРОВАТЬ из коллекций. Синхронизированная коллекция

public Object[] toArray() {
    synchronized(mutex) {return c.toArray();}
}

Как видноблокировка удерживается на протяжении всего времени выполнения операции.Как таковая безопасная копия данных берется.Неважно, если Итератор используется внутри.Возвращенный массив можно использовать в поточно-ориентированном режиме, поскольку только локальный поток имеет ссылку на него.

ПРИМЕЧАНИЕ. Если вы хотите избежать этих проблем, я предлагаю использовать Set из библиотеки параллелизма, добавленной в Java.5.0 в 2004 году. Я также предлагаю вам использовать дженерики, поскольку это может сделать ваши коллекции более безопасными.

...