Java ArrayList / Строка / атомарное переменное ориентированное на многопотоковое исполнение чтение?

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

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

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

12
задан RedBlueThing 27 September 2011 в 08:42
поделиться

6 ответов

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

13
ответ дан 2 December 2019 в 05:54
поделиться

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

2
ответ дан 2 December 2019 в 05:54
поделиться

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

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

2
ответ дан 2 December 2019 в 05:54
поделиться

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

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

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

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

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

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

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

5
ответ дан 2 December 2019 в 05:54
поделиться

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

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

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

3
ответ дан 2 December 2019 в 05:54
поделиться

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

1
ответ дан 2 December 2019 в 05:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: