Является ли Java ArrayList/String/atomic переменной чтения потоком безопасным?

Я обдумывал это и читал, но могу найти абсолютный авторитетный ответ.

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

Мне интересно, если чтение данных в этих структурах является потокобезопасным; то есть безопасно ли рекурсивно читать переменные из объектов, перебирать ArrayLists и т.д. для извлечения информации из структур в нескольких потоках без синхронизации?

Ответ 1

Единственная причина, почему это было бы небезопасно, - это когда один поток писал в поле, а другой поток одновременно читал его. No состояние гонки существует, если данные не изменяются. Создание объектов, неизменяемых, является одним из способов гарантировать безопасность потоков. Начните с чтения этой статьи от IBM.

Ответ 2

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

Любые данные, которые совместно используются потоком, требуют "барьера памяти" для обеспечения его видимости. Это можно сделать несколькими способами.

Сначала любой член, объявленный final и инициализированный в конструкторе, видим для любого потока после завершения конструктора.

Изменения в любом члене, объявленном volatile, видны для всех потоков. По сути, запись "размывается" из любого кеша в основную память, где ее можно увидеть с помощью любого потока, который обращается к основной памяти.

Теперь это становится немного сложнее. Любые записи, сделанные потоком до того, как этот поток записывается в изменчивую переменную, также очищаются. Аналогично, когда поток читает изменчивую переменную, его кеш очищается, а последующие чтения могут переписывать его из основной памяти.

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

Одним из способов сделать эту работу является то, что поток, который заполняет вашу общую структуру данных, присваивает результат переменной volatile (или AtomicReference или другому подходящему объекту java.util.concurrent). Когда другие потоки получают доступ к этой переменной, они не только гарантируют получение последнего значения для этой переменной, но также любые изменения, внесенные в структуру данных потоком, прежде чем назначить значение переменной.

Ответ 3

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

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

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

Ответ 4

Как добавляет добавление всем остальным: если вы уверены, что вам нужно синхронизировать списки массивов, вы можете вызвать Collections.synchronizedList(myList), который вернет вам реалистичную реализацию потока.

Ответ 5

Я не вижу, как должна быть проблема с чтением из ArrayLists, Strings и примитивных значений с использованием нескольких потоков.

Пока вы только читаете, синхронизация не требуется. Для строк и примитивов это, безусловно, безопасно, поскольку они неизменны. Для ArrayLists это должно быть безопасно, но у меня нет его полномочий.

Ответ 6

Do НЕ использовать java.util.Vector, используйте обертку java.util.Collections.unmodifiableXXX(), если они действительно не поддаются изменению, это гарантирует, что они не изменятся и будут выполнять этот контракт. Если они будут изменены, используйте java.util.Collections.syncronizedXXX(). Но это гарантирует только безопасность внутренней резьбы. Создание переменных final также поможет компилятору /JIT с оптимизацией.