Что такое атомарность в Java

Я изучаю параллелизм JAVA на практике и нашел следующее определение атомной операции:

Operation A, B are atomic with respect to each other if from the perspective of thread executing A, when another thread executes B, either all of B has executed or none of it has. An atomic operation is one that is atomic with respect to all operation, including itself, that operates on the same data.

Допустим, у меня есть два потока A и B, оба из которых обращаются к счетчику переменных AtomicLong. Допустим, поток A читает count, а поток B был занят выполнением операции count.incrementAndGe(). В таком сценарии согласно приведенному выше определению (либо все B выполнено, либо ни одно из них):

A) ThreadA видит предыдущее значение count (потому что count еще не обновлен)

Или

Б) ThreadA будет ждать, пока ThreadB завершит операцию и увидит последнее значение.

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

Это не будет ждать. В зависимости от точного времени поток A увидит значение либо до того, как поток B увеличит его, либо после его увеличения. Вы не можете предсказать, какой. Но одна из гарантий атомарности состоит в том, что поток A не увидит какое-то странное промежуточное состояние. (Вторая гарантия заключается в том, что если два потока одновременно выполняют приращения, оба будут выполняться правильно... в некотором порядке. Никакие «счетчики» не будут потеряны, лишние не будут добавлены.)

Stephen C 21.03.2022 15:22

@StephenC Это означает, что если я выполняю count.incrementAndGet() в своем методе, который выполняется несколькими потоками, у меня все еще может быть правильное условие гонки. Потому что оба потока A и B могут прочитать одно и то же значение, и один из них перезапишет другой. По сути, я получу один шаг вместо двух.

Tarun 21.03.2022 15:27

Нет. Не будешь. Смотрите мое обновление к моему предыдущему комментарию. Но результат того или иного вызова incrementAndGet не будет включать оба приращения.

Stephen C 21.03.2022 15:28

К вашему сведению… См. Википедию: Линеаризуемость и соответствующую заметку Атомарность (системы баз данных).

Basil Bourque 21.03.2022 16:01
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Описанная вами ситуация называется «гонка данных». Либо поток A выиграет гонку (он увидит начальное значение), либо поток B выиграет гонку (поток A увидит конечное значение). Если вы не предоставили какие-то явные средства, чтобы заставить потоки обращаться к переменной в определенном порядке. , то невозможно узнать, кто выиграет гонку.


«Атомарность» означает, что поток B либо увидит данные такими, какими они были до атомарной операции, либо увидит их после завершения операции. Поток B никогда не увидит его в состоянии наполовину выполненного.

Для обновления до одного 64-битного значения long. Практически единственный способ сделать это «наполовину» — это если 64-битное значение будет разорванный на 32-битном оборудовании. Он был бы «разорван», если бы одно из двух 32-битных слов, составляющих 64-битное значение, было обновлено, а другое — нет.

Да, спасибо за ваш ответ. Но я не могу связать это определение с тем, как я могу правильно увеличить переменную AtomicLong или AtomicInt. Допустим, ThreadA и ThreadB оба выполняют getAndIncrement. ThreadB должен иметь возможность видеть предыдущее значение, пока threadA занят выполнением приращения, что означает, что количество приращений будет потеряно.

Tarun 21.03.2022 15:37

@Tarun, в случае, когда оба потока выполняют атомарность getAndIncrement(), атомарность работает в обоих направлениях. Если поток A выигрывает гонку, то поток B не будет обращаться к переменной до тех пор, пока поток A не завершит всю операцию (т. е. пока поток A не отправит увеличенное значение). переменная, пока B не закончится. Значение, возвращаемое победителю, будет исходным значением. Значение, возвращаемое проигравшему, будет исходным +1, а окончательный счет будет исходным +2. Всегда.

Solomon Slow 21.03.2022 15:43

Один вопрос. Когда вы говорите «Выигрывает гонку», вы имеете в виду того, кто первым прочитал значение в контексте getAndInrement?

Tarun 21.03.2022 15:51

Спасибо. Теперь это имеет полный смысл. «Выигрывает гонку» означает тот, кто первым выполнил операцию.

Tarun 21.03.2022 16:08

@ Тарун, да. Если поток A «сначала считывает значение», то поток A должен записать обратно увеличенное значение, прежде чем потоку B будет разрешено его прочитать, и наоборот. Весь вызов функции getAndIncrement() происходит атомарно: когда какой-то поток T делает это, буквально есть нет идентифицируемого момента времени в любом другом потоке, который возникает между моментом запуска T и моментом завершения T.

Solomon Slow 21.03.2022 16:08

Небольшая поправка: это не гонка данных, а состояние гонки. Гонки данных происходят, когда у вас есть конфликтующие действия, которые не упорядочены краем «происходит до». Нестабильные операции чтения/записи являются операциями синхронизации и исключаются из гонки данных.

pveentjer 22.03.2022 19:47

Другие вопросы по теме