Делает ли неизменяемая оболочка для коллекций Java их потокобезопасными?

Мне нужно сделать ArrayList из ArrayLists потокобезопасным. Я также не могу позволить клиенту вносить изменения в коллекцию. Сделает ли неизменяемая оболочка потокобезопасной или мне нужны две оболочки для коллекции?


person WolfmanDragon    schedule 17.09.2008    source источник


Ответы (9)


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

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

person Michael Borgwardt    schedule 17.09.2008
comment
Я так многому научился с тех пор, как опубликовал этот вопрос. Просто доказывает, что только потому, что ответ принят и / или популярен, он правильный. Многие неправильные ответы на этом сайте принимаются только потому, что они популярны. Я изменил принятый ответ на этот после того, как узнал больше. - person WolfmanDragon; 05.02.2009

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

if(queue.Count > 0)
{
   queue.Add(...);
}
person Dror Helper    schedule 18.09.2008

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

person Dan Dyer    schedule 17.09.2008

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

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

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

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

person John Gardner    schedule 17.09.2008

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

person djpowell    schedule 17.09.2008

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

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

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

person Chris Vest    schedule 21.09.2008

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

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

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

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

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

person volley    schedule 18.09.2008
comment
Однако обернутая коллекция не является неизменной, пока другой поток поддерживает ссылку на изменяемую версию. Этот другой поток может изменить коллекцию, и с синхронизацией нет гарантии, что другие потоки увидят изменения. - person Eddie; 24.01.2009
comment
Эдди: Вы конечно правы (кроме того, что Вы имели в виду без синхронизации?); В то время я писал код, раскрывающий списки только для чтения, и поэтому неявно (но, вероятно, неправильно) предполагал, что никаких дальнейших правок вноситься не будет. Спасибо! - person volley; 03.07.2011

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

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

person Mecki    schedule 17.09.2008

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

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

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

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

person Andreas Petersson    schedule 17.09.2008