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