2011-01-03 2 views
4

Представьте синхронизируетсяCollection:Как клонировать синхронизированную коллекцию?

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

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

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

+0

Каковы условия копии? Можно ли изменить исходный набор при копировании? Что вы пытаетесь выполнить клон? (возможно, сможет предложить более эффективный способ достижения ваших целей, но вам нужно ответить на эти вопросы, чтобы убедиться, что это правильно для вас) –

+0

@Berin Loritsch: Да, исходный набор может меняться во время копирования. Я пытаюсь получить Итератор из набора, который должен быть независим от оригинальной коллекции в смысле данных и синхронизации. – MRalwasser

ответ

6

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

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

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

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

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

+3

К сожалению, это использует Итератор под капотом, поэтому вам нужно синхронизировать (делать) это безопасно. –

+0

@Peter да, очевидно, вряд ли когда-либо можно получить доступ к коллекции без использования итераторов –

+1

Плохая сторона истории заключается в том, что вы должны знать, как этот метод реализован. – Bozho

1

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

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

EDIT От Collections.SynchronizedCollection

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

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

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

+0

все еще использует итератор. – Bozho

+0

Правда, но он перебирает копию. это потокобезопасность. toArray() является потокобезопасным, потому что он является атомарным. –

+0

Я ничего не вижу, что гарантирует, что 'toArray()' является атомарным - по крайней мере в том смысле, что имеет значение в этом случае. Единственная гарантия, которую я вижу, заключается в том, что вызов 'toArray()' создает новую копию массива, даже если коллекция поддерживается массивом. Я бы не захотел поставить мой код на это предположение. Требуется время для копирования массивов, особенно если источник поддерживается узлами (как в случае со всеми наборами и LinkedLists). В этом случае потребуется итератор. –

3

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

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

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

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

EDIT: Я только что заметил, что ConcurrentSkipListSet является Cloneable и предоставляет метод threadafe clone(). Я изменил свой ответ, потому что я действительно считаю, что это лучший вариант - вместо потери масштабируемости и производительности до Collections.concurrentSet().

Смежные вопросы