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