Безопасен ли поток чтения Java ArrayList / String / atomic? - PullRequest
12 голосов
/ 05 марта 2010

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

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

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

Ответы [ 6 ]

13 голосов
/ 05 марта 2010

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

5 голосов
/ 05 марта 2010

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

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

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

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

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

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

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

3 голосов
/ 05 марта 2010

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

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

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

2 голосов
/ 05 марта 2010

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

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

2 голосов
/ 05 марта 2010

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

1 голос
/ 05 марта 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...