Функция атом, если это происходит мгновенно. [1]
Здесь «кажется» означает с точки зрения остальной части системы. Например, рассмотрим синхронизированную функцию, которая меняет связанный список. Внешнему наблюдателю операция явно не происходит мгновенно: для обновления всех указателей списка требуется много чтений и записей. Однако, поскольку блокировка сохраняется все время, никакая другая часть системы не может прочитать список за это время, поэтому для них обновление появляется мгновенно.
Аналогично, операции CAS (сравнение и набор) не фактически происходят мгновенно на современных компьютерах. Требуется время для одного ядра ЦП для получения исключительного права на запись в значение, а затем для получения нового значения для другого ядра требуется дополнительное время для повторного получения доступа к чтению, чтобы увидеть новое значение. В течение этого времени CPU выполняет другие команды параллельно.Чтобы гарантировать иллюзию мгновенного выполнения, JVM выдает инструкции ЦП до и после операции CAS, чтобы гарантировать, что логически последующие чтения не будут завершены и выполнены до завершения CAS (что позволит вам прочитать часть связанного списка до вы, например, сделали блокировку), и что логически предшествующие записи не задерживаются и не выполняются после завершения CAS (что позволило бы другому потоку блокировать до того, как связанный список был полностью обновлен).
Эти инструкции по заказу ЦП - это ключевое различие между AtomicInteger.compareAndSet и AtomicInteger.weakCompareAndSet (бит «может не срабатывать ложно» легко выпрямляется с помощью петли). Без гарантий упорядочения слабая операция CAS не может использоваться для реализации большинства параллельных алгоритмов и «редко является подходящей альтернативой compareAndSet».
Если это звучит сложно ... ну ... это! Вот почему you can still get a PhD by designing a concurrent algorithm. Чтобы показать правильность для параллельного алгоритма, вы должны подумать о том, что может сделать любой другой поток, чтобы помешать вам. Это может помочь, если вы думаете о них как о противниках, пытаясь сломать иллюзию атомарности. Например, давайте рассмотрим ваш пример:
if (tail_index.get() == (head_index.getAndIncrement()))
Я предполагаю, что это является частью методы поп элемента от стека реализован как циклический массив со счетчиками индекса, и выполнить тело «если», если стек теперь пуст. Поскольку head_index и tail_index доступны отдельно, ваш противник может «разделить» их на столько операций, сколько ему нравится. (Предположим, например, что ваш поток прерывается ОС между get и getAndIncrement.) Таким образом, ему было бы легко добавить десятки элементов в стек, а затем удалить все, кроме одного, оставив head_index выше tail_index ; ваш блок if никогда не будет выполнен, даже если вы удаляете последний элемент в стеке.
Итак, когда ваша книга говорит, что get(), getAndSet() и т. Д. Являются атомарными, это не делает общее утверждение о возможной реализации этих методов. Это говорит вам, что стандарт Java гарантирует, что они являются атомарными и делает это, тщательно используя доступные инструкции ЦП, таким образом, что было бы невозможно сделать в простой Java (synchronized
позволяет вам эмулировать его, но более дорогостоящим).
Атомные средства «неделимые». Вы не можете видеть части атомной операции, все, что она делает, кажется, происходит сразу. В вашем случае нет ничего, чтобы ваши два метода get выполнялись как один, поэтому я бы не назвал его атомарным. –