Я изучаю параллелизм 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, потому что в противном случае у нас все еще может быть состояние гонки.
@StephenC Это означает, что если я выполняю count.incrementAndGet() в своем методе, который выполняется несколькими потоками, у меня все еще может быть правильное условие гонки. Потому что оба потока A и B могут прочитать одно и то же значение, и один из них перезапишет другой. По сути, я получу один шаг вместо двух.
Нет. Не будешь. Смотрите мое обновление к моему предыдущему комментарию. Но результат того или иного вызова incrementAndGet не будет включать оба приращения.
Описанная вами ситуация называется «гонка данных». Либо поток A выиграет гонку (он увидит начальное значение), либо поток B выиграет гонку (поток A увидит конечное значение). Если вы не предоставили какие-то явные средства, чтобы заставить потоки обращаться к переменной в определенном порядке. , то невозможно узнать, кто выиграет гонку.
«Атомарность» означает, что поток B либо увидит данные такими, какими они были до атомарной операции, либо увидит их после завершения операции. Поток B никогда не увидит его в состоянии наполовину выполненного.
Для обновления до одного 64-битного значения long
. Практически единственный способ сделать это «наполовину» — это если 64-битное значение будет разорванный на 32-битном оборудовании. Он был бы «разорван», если бы одно из двух 32-битных слов, составляющих 64-битное значение, было обновлено, а другое — нет.
Да, спасибо за ваш ответ. Но я не могу связать это определение с тем, как я могу правильно увеличить переменную AtomicLong или AtomicInt. Допустим, ThreadA и ThreadB оба выполняют getAndIncrement. ThreadB должен иметь возможность видеть предыдущее значение, пока threadA занят выполнением приращения, что означает, что количество приращений будет потеряно.
@Tarun, в случае, когда оба потока выполняют атомарность getAndIncrement()
, атомарность работает в обоих направлениях. Если поток A выигрывает гонку, то поток B не будет обращаться к переменной до тех пор, пока поток A не завершит всю операцию (т. е. пока поток A не отправит увеличенное значение). переменная, пока B не закончится. Значение, возвращаемое победителю, будет исходным значением. Значение, возвращаемое проигравшему, будет исходным +1, а окончательный счет будет исходным +2. Всегда.
Один вопрос. Когда вы говорите «Выигрывает гонку», вы имеете в виду того, кто первым прочитал значение в контексте getAndInrement?
Спасибо. Теперь это имеет полный смысл. «Выигрывает гонку» означает тот, кто первым выполнил операцию.
@ Тарун, да. Если поток A «сначала считывает значение», то поток A должен записать обратно увеличенное значение, прежде чем потоку B будет разрешено его прочитать, и наоборот. Весь вызов функции getAndIncrement()
происходит атомарно: когда какой-то поток T делает это, буквально есть нет идентифицируемого момента времени в любом другом потоке, который возникает между моментом запуска T и моментом завершения T.
Небольшая поправка: это не гонка данных, а состояние гонки. Гонки данных происходят, когда у вас есть конфликтующие действия, которые не упорядочены краем «происходит до». Нестабильные операции чтения/записи являются операциями синхронизации и исключаются из гонки данных.
Это не будет ждать. В зависимости от точного времени поток A увидит значение либо до того, как поток B увеличит его, либо после его увеличения. Вы не можете предсказать, какой. Но одна из гарантий атомарности состоит в том, что поток A не увидит какое-то странное промежуточное состояние. (Вторая гарантия заключается в том, что если два потока одновременно выполняют приращения, оба будут выполняться правильно... в некотором порядке. Никакие «счетчики» не будут потеряны, лишние не будут добавлены.)