Мне нужно сделать ArrayList из ArrayLists потокобезопасным. Я также не могу позволить клиенту вносить изменения в коллекцию. Сделает ли неизменяемая оболочка потокобезопасной или мне нужны две оболочки для коллекции?
Делает ли неизменяемая оболочка для коллекций Java их потокобезопасными?
Ответы (9)
По-разному. Оболочка будет предотвращать изменения только коллекции, которую она обертывает, но не объектов в коллекции. Если у вас есть ArrayList из ArrayLists, глобальный список, а также каждый из его списков элементов необходимо обернуть отдельно, и вам также может потребоваться что-то сделать для содержимого этих списков. Наконец, вы должны убедиться, что исходные объекты списка не изменились, поскольку оболочка предотвращает изменения только через ссылку на оболочку, а не на исходный объект.
В этом случае вам НЕ нужна синхронизированная оболочка.
По связанной теме - я видел несколько ответов, предлагающих использовать синхронизированную коллекцию для обеспечения безопасности потоков. Использование синхронизированной версии коллекции не делает ее «потокобезопасной» — хотя каждая операция (вставка, подсчет и т. д.) защищена мьютексом, при объединении двух операций нет гарантии, что они будут выполняться атомарно. Например, следующий код не является потокобезопасным (даже с синхронизированной очередью):
if(queue.Count > 0)
{
queue.Add(...);
}
Немодифицируемая оболочка предотвращает изменения только в структуре списка, к которому она применяется. Если этот список содержит другие списки и у вас есть потоки, пытающиеся изменить эти вложенные списки, то вы не защищены от рисков одновременного изменения.
Глядя на исходный код коллекций, похоже, что неизменяемый не делает его синхронизированным.
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable;
static class UnmodifiableCollection<E> implements Collection<E>, Serializable;
в оболочках синхронизированных классов есть объект мьютекса для выполнения синхронизированных частей, поэтому похоже, что вам нужно использовать оба, чтобы получить оба. Или свернуть самостоятельно!
Я полагаю, что, поскольку оболочка UnmodifiableList сохраняет ArrayList в конечном поле, любые методы чтения в оболочке будут видеть список таким, каким он был, когда оболочка была создана, до тех пор, пока список не будет изменен после создания оболочки, и как пока изменяемые ArrayLists внутри оболочки не изменяются (от чего оболочка не может защитить).
Будет потокобезопасно, если немодифицируемое представление будет безопасно опубликовано, а модифицируемый оригинал никогда не будет изменен (включая все объекты, рекурсивно содержащиеся в коллекции!) после публикации немодифицируемого представления.
Если вы хотите продолжать изменять оригинал, вы можете либо создать защитную копию графа объектов вашей коллекции и вернуть неизменяемое представление этого, либо использовать для начала потокобезопасный список и вернуть неизменяемое представление тот.
Вы не можете вернуть неизменяемый список(синхронизированный список(theList)) , если вы все еще намереваетесь получить доступ к несинхронизированному списку впоследствии; если изменяемое состояние совместно используется несколькими потоками, то все потоки должны синхронизироваться с одними и теми же блокировками при доступе к этому состоянию.
Неизменяемый объект по определению потокобезопасен (при условии, что никто не сохраняет ссылки на исходные коллекции), поэтому синхронизация нет необходимости.
Обертывание внешнего ArrayList с помощью Collections.unmodifiableList() не позволяет клиенту изменять его содержимое (и, таким образом, делает его потокобезопасным), но внутренние ArrayList по-прежнему изменяемы.
Обертывание внутренних ArrayLists с помощью Collections.unmodifiableList() также предотвращает изменение клиентом их содержимого (и, таким образом, делает их потокобезопасными), что вам и нужно.
Дайте нам знать, если это решение вызывает проблемы (накладные расходы, использование памяти и т. д.); другие решения могут быть применимы к вашей проблеме. :)
РЕДАКТИРОВАТЬ: Конечно, если списки изменены, они НЕ являются потокобезопасными. Я полагал, что никаких дальнейших правок не будет.
Не уверен, что понял, что вы пытаетесь сделать, но я бы сказал, что в большинстве случаев ответ "Нет".
Если вы настроите ArrayList из ArrayList и обоих, внешний и внутренний списки никогда не могут быть изменены после создания (и во время создания только один поток будет иметь доступ к внутреннему и внешнему спискам), они, вероятно, потокобезопасны оболочкой (если оба , внешний и внутренний списки завернуты таким образом, что их изменение невозможно). Все операции только для чтения в ArrayLists, скорее всего, потокобезопасны. Однако Sun не гарантирует их потокобезопасность (также не для операций только для чтения), поэтому даже если они могут работать прямо сейчас, в будущем они могут сломаться (если Sun создаст некоторые внутренние кэширование данных для более быстрого доступа, например).
Это необходимо, если:
- Есть еще ссылка на исходный изменяемый список.
- Доступ к списку, возможно, будет осуществляться через итератор.
Если вы собираетесь читать из ArrayList только по индексу, вы можете предположить, что это потокобезопасно.
Если вы сомневаетесь, выберите синхронизированную оболочку.