Делает ли немодифицируемая оболочка для коллекций java потокобезопасными? - PullRequest
19 голосов
/ 18 сентября 2008

Мне нужно сделать поток ArrayList из ArrayLists безопасным. У меня также не может быть клиента, делающего изменения в коллекции. Поможет ли немодифицируемый упаковщик сделать его безопасным для потоков или мне понадобятся два упаковщика в коллекции?

Ответы [ 9 ]

10 голосов
/ 18 сентября 2008

Это зависит. Оболочка будет предотвращать только изменения в коллекции, которую она упаковывает, а не в объекты в коллекции. Если у вас есть ArrayList из ArrayLists, глобальный список, а также каждый из его списков элементов должны быть обернуты отдельно, и вам, возможно, придется что-то сделать для содержимого этих списков. Наконец, вы должны убедиться, что исходные объекты списка не изменены, поскольку оболочка предотвращает изменения только через ссылку на оболочку, а не на исходный объект.

В этом случае вам НЕ нужна синхронизированная оболочка.

5 голосов
/ 18 сентября 2008

По связанной теме - я видел несколько ответов, в которых предлагалось использовать синхронизированный сбор для обеспечения безопасности потоков. Использование синхронизированной версии коллекции не делает ее «поточно-ориентированной» - хотя каждая операция (вставка, подсчет и т. Д.) Защищена мьютексом при объединении двух операций, нет гарантии, что они будут выполняться атомарно. Например, следующий код не является потокобезопасным (даже с синхронизированной очередью):

if(queue.Count > 0)
{
   queue.Add(...);
}
2 голосов
/ 18 сентября 2008

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

1 голос
/ 21 сентября 2008

Будет поточно-ориентированным, если немодифицируемое представление безопасно публикуется, а модифицируемый оригинал никогда не изменяется (включая все объекты, рекурсивно содержащиеся в коллекции!) После публикации неизменяемого представления.

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

Вы не можете вернуть unmodifiableList (synchonizedList (theList)), если вы все еще намереваетесь получить доступ к списку несинхронизированных впоследствии; если изменяемое состояние совместно используется несколькими потоками, то все потоки должны синхронизироваться при одинаковых блокировках при обращении к этому состоянию.

1 голос
/ 18 сентября 2008

Неизменяемый объект по определению является потокобезопасным (при условии, что никто не сохраняет ссылки на исходные коллекции), поэтому синхронизация не необходима.

Обтекание внешнего ArrayList с использованием Collections.unmodifiableList () предотвращает изменение клиентом своего содержимого (и, таким образом, делает его потоком безопасно), но внутренние списки ArrayList по-прежнему могут изменяться.

Обтекание внутренних списков ArrayLists с использованием Collections.unmodifiableList () предотвращает изменение клиентом их содержимого (и, следовательно, делает их потокобезопасный), что вам нужно.

Сообщите нам, если это решение вызывает проблемы (накладные расходы, использование памяти и т. Д.); другие решения могут быть применимы к вашей проблеме. :)

РЕДАКТИРОВАТЬ: Конечно, если списки изменены, они НЕ являются потокобезопасными. Я предполагал, что дальнейшие правки не будут сделаны.

1 голос
/ 18 сентября 2008

Я полагаю, что поскольку оболочка UnmodifiableList сохраняет ArrayList в конечном поле, любые методы чтения в оболочке будут видеть список таким, каким он был, когда оболочка была создана, пока список не был изменен после создания оболочки. и до тех пор, пока изменяемые списки ArrayList внутри оболочки не изменяются (от чего оболочка не может защитить).

1 голос
/ 18 сентября 2008

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

static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
                 implements Set<E>, Serializable;

static class UnmodifiableCollection<E> implements Collection<E>, Serializable;

Обернутые синхронизированные классы имеют объект мьютекса для выполнения синхронизированных частей, так что похоже, что вам нужно использовать оба, чтобы получить оба. Или сверните свое собственное!

0 голосов
/ 18 сентября 2008

Не уверен, понял ли я, что вы пытаетесь сделать, но я бы сказал, что ответ в большинстве случаев - "Нет".

Если вы устанавливаете ArrayList из ArrayList и оба, внешний и внутренний списки никогда не могут быть изменены после создания (и во время создания только один поток будет иметь доступ к внутреннему и внешнему спискам), они, вероятно, являются потокобезопасными для оболочки (если оба, внешний и внутренний списки обернуты таким образом, что их изменение невозможно). Все операции только для чтения на ArrayLists, скорее всего, потокобезопасны. Однако Sun не гарантирует, что они будут поточно-ориентированными (также не для операций только для чтения), поэтому даже если это может сработать прямо сейчас, в будущем это может прерваться (если Sun создаст некоторое внутреннее кэширование). данных для быстрого доступа, например).

0 голосов
/ 18 сентября 2008

Это необходимо, если:

  1. До сих пор есть ссылка на оригинальный изменяемый список.
  2. Возможно, список будет доступен через итератор.

Если вы собираетесь читать из ArrayList только по индексу, вы можете предположить, что это потокобезопасно.

Если сомневаетесь, выберите синхронизированную оболочку.

...