Потоковая безопасность параллельных безопасных контейнеров, где значения изменчивы - PullRequest
0 голосов
/ 11 октября 2018

Скажем, у меня есть карта, которая обеспечивает безопасность потока (полный параллелизм поиска) ConcurrentHashMap<String, Foo>, где извлекаемые значения - это некоторые изменяемые объекты

class Foo { 
    public Object bar;
}

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

Foo foo = new Foo();
foo.bar = "Test";
concurrentMap.add("key", foo);

В отдельном потоке работа выполняется путем просмотра значений на карте (при условии, что здесь предварительно установлены правильные значения)

System.out.println(concurrentMap.get("key").bar);

Есть ли проблемы с доступом к Foo::bar в этой ситуации?Есть ли сценарий, когда это может не сработать, как ожидалось?

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Операции только для чтения являются потокобезопасными по определению.Условие гонки или гонки данных произойдет, только если есть хотя бы один поток, который изменяет данные.

Так что, если вы поместите значение в карту, прежде чем создавать поток, который считывает значение из карты,тогда операция полностью безопасна.И если вы никогда не изменяете bar или его ссылки внутри foo, у вас тоже все будет хорошо.Вам даже не нужна параллельная карта в этом случае.Обычная карта просто подойдет.

Однако вы можете получить неожиданные результаты, если bar модифицируется или если ссылка на bar изменяется внутри foo.Вот пример чего-то, что может пойти не так.Предположим, что bar - это long.

class Foo { 
    public long bar;
}

И у вас есть поток 1, который делает:

Foo foo = concurrentMap.get("key");
.....
..... /// some code
System.out.println(foo.bar);

И есть еще один поток в фоновом режиме, который делает это:

Foo foo = concurrentMap.get("key");
.....
long newBar = foo.bar + 1;
foo.bar = newBar;

Здесь у вас прямое состояние гонки.

Теперь, если поток 2 фактически просто делает это вместо этого:

 Foo foo = concurrentMap.get("key");
 .....
 long newBar = rand.nextLong();
 foo.bar = newBar;

У вас нет условия гонки, но у вас естьвместо этого - гонка данных, потому что long - это 64-битная версия, и компилятор может выполнить присвоение long и double как две операции.

Есть много других сценариев, где это может пойти не так, и действительно труднорассуждать о них, не очень осторожно

Так что, по крайней мере, вы должны сделать бар изменчивым, как это:

class Foo { 
    public volatile Object bar;
}

И быть очень осторожным в том, как вы работаете на баре и чтовнутри, если это реальный объект какого-то класса.

Если вы хотите больше узнать о гонках с данными и лучше понять условия гонки, вам следует проверить этот превосходный курс https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

Имеет секретоб этом, это объясняет это очень хорошо, с действительно хорошими примерами.

Вы также должны проверить официальную документацию модели памяти Java https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4

Надеюсь, это поможет

0 голосов
/ 11 октября 2018

Он должен работать так, как если значения и извлечение из параллельного хэш-карты должны быть установлены, то должно быть установлено отношение «до того, как это произойдет» (см. Гарантируется ли, что ConcurrentHashMap.get () увидит предыдущий ConcurrentHashMap.put () другим потоком? ).Поток потребителя должен видеть значение бара, как это было до вставки карты.

Лично я бы постарался сделать объекты Foo (и bar) неизменяемыми (почему бы и нет, если они никогда не изменятся?).

...