Я искал более детальный ответ на вопрос: «Каковы ограничения каждого?». Например, если is - флаг, установленный одним потоком и прочитанный одним или несколькими другими, в AtomicBoolean нет необходимости. Однако, как я вижу из этих ответов, если поток совместно использует переменную в нескольких потоках, которые могут записывать и действуют в результате их чтения, AtomicBoolean вводит в игру операции неблокирования типа CAS. На самом деле, я немного учусь здесь. Надеюсь, другие тоже выиграют.
volatile boolean потребуется явная синхронизация для обработки условий гонки, другими словами, сценарий, такой как общий ресурс, обновляется (изменение состояния) несколькими потоками, например, счетчик приращения / уменьшения или переключение логического значения.
Они просто совершенно разные. Рассмотрим пример целого volatileчисла:
volatileint i =0;void incIBy5(){
i +=5;}
Если два потока вызывают функцию одновременно, iможет быть 5 впоследствии, так как скомпилированный код будет чем-то похожим на это (за исключением того, что вы не можете выполнить синхронизацию int):
void incIBy5(){int temp;synchronized(i){ temp = i }synchronized(i){ i = temp +5}}
Если переменная является изменчивой, каждый атомарный доступ к ней синхронизируется, но не всегда очевидно, что на самом деле квалифицируется как атомарный доступ. С Atomic*объектом гарантируется, что каждый метод является «атомарным».
Таким образом, если вы используете AtomicIntegerи getAndAdd(int delta), вы можете быть уверены, что результат будет 10. Таким же образом, если два потока одновременно отменяют booleanпеременную одновременно, с помощью параметра AtomicBooleanвы можете быть уверены, что впоследствии оно будет иметь исходное значение, а с помощью volatile boolean- вы не сможете.
Поэтому, когда у вас есть несколько потоков, модифицирующих поле, вам нужно сделать его атомарным или использовать явную синхронизацию.
Если у вас есть работающий поток loop()и другой поток, вызывающий stop(), вы можете столкнуться с бесконечным циклом, если пропустите его volatile, поскольку первый поток может кэшировать значение stop. Здесь это volatileслужит подсказкой компилятору, чтобы он был немного осторожнее с оптимизацией.
-1: вы приводите примеры, но не объясняете разницу между volatile и Atomicxxxx.
Джейсон С
70
Вопрос не в этом volatile. Вопрос о volatile booleanпротив AtomicBoolean.
дольмен
26
-1: вопрос, заданный специально для логического типа, который является уникальным случаем по сравнению с другими типами данных и должен быть объяснен напрямую.
Джон Хаагер
8
@ sgp15 Это связано с синхронизацией, начиная с Java 5.
Человек с односторонним
6
Если логическое значение читается многими потоками, но записывается только одним потоком, то этого volatile booleanдостаточно. Если есть также много авторов, вам может понадобиться AtomicBoolean.
StvnBrkdll
263
Я использую изменчивые поля, когда упомянутое поле ТОЛЬКО ОБНОВЛЯЕТСЯ потоком его владельца, а значение читается только другими потоками, вы можете рассматривать его как сценарий публикации / подписки, в котором есть много наблюдателей, но только один издатель. Однако, если эти наблюдатели должны выполнить некоторую логику, основанную на значении поля, а затем отодвинуть новое значение, тогда я использую Atomic * переменные или блокировки или синхронизированные блоки, что мне больше подходит. Во многих параллельных сценариях он сводится к получению значения, сравнению его с другим и обновлению, если необходимо, следовательно, методы CompareAndSet и getAndSet присутствуют в классах Atomic *.
Проверьте JavaDocs пакета java.util.concurrent.atomic для получения списка классов Atomic и отличного объяснения того, как они работают (только что узнали, что они свободны от блокировок, поэтому они имеют преимущество перед блокировками или синхронизированными блоками)
Это правда, но не будет ли это довольно редким требованием для логического выражения?
Робин
1
@Robin подумает об использовании его для управления отложенным вызовом метода инициализации.
Устаман Сангат
На самом деле я думаю, что это один из основных вариантов использования.
fool4jesus
42
AtomicBooleanимеет методы, которые выполняют свои составные операции атомарно и без использования synchronizedблока. С другой стороны, volatile booleanможет выполнять сложные операции, только если это делается внутри synchronizedблока.
Эффекты памяти чтения / записи , чтобы volatile booleanидентичны getи setметодов AtomicBooleanсоответственно.
Например, compareAndSetметод будет атомарно выполнять следующее (без synchronizedблока):
if(value == expectedValue){
value = newValue;returntrue;}else{returnfalse;}
Следовательно, compareAndSetметод позволит вам написать код, который гарантированно будет выполняться только один раз, даже если он вызывается из нескольких потоков. Например:
Гарантируется, что он будет уведомлять слушателя только один раз (при условии, что ни один другой поток не установит AtomicBooleanего falseснова после того, как он будет установлен true).
volatileКлючевое слово гарантирует связь между потоками, разделяющими эту переменную. Это не гарантирует, что 2 или более потоков не будут прерывать друг друга при доступе к этой логической переменной.
Логический (как в примитивном типе) доступ является атомарным в Java. Оба читает и задания. Так что никакой другой поток не будет «прерывать» логические операции.
Maciej Biłas
1
Извините, но как это ответит на вопрос? Atomic*Класс обертывание volatileполя.
Серый
Разве кэши ЦП не являются основным фактором для установки volatile? Чтобы убедиться, что значение на самом деле соответствует тому, которое было установлено в последнее время
jocull
8
Летучий логический против AtomicBoolean
Классы Atomic * обертывают летучий примитив того же типа. Из источника:
publicclassAtomicLongextendsNumberimplements java.io.Serializable{...privatevolatilelong value;...publicfinallong get(){return value;}...publicfinalvoid set(long newValue){
value = newValue;}
Так что, если все, что вы делаете, это получаете и настраиваете Atomic *, то вместо этого у вас может быть просто изменчивое поле.
Что делает AtomicBoolean, чего не может достичь изменчивое логическое значение?
Атомные * классы предоставляют методы , которые обеспечивают более расширенные функциональные возможности, такие как incrementAndGet(), compareAndSet()и другие , которые реализуют несколько операций (GET / инкремент / комплект, тест / набора) без блокировки. Вот почему классы Atomic * такие мощные.
Например, если несколько потоков используют следующий код с использованием ++, будут условия состязания, потому что ++на самом деле: get, increment и set.
privatevolatile value;...// race conditions here
value++;
Однако следующий код будет безопасно работать в многопоточной среде без блокировок:
privatefinalAtomicLong value =newAtomicLong();...
value.incrementAndGet();
Также важно отметить, что перенос вашего изменчивого поля с использованием класса Atomic * является хорошим способом инкапсуляции критического общего ресурса с точки зрения объекта. Это означает, что разработчики не могут просто иметь дело с полем, предполагая, что оно не является общим, что может привести к проблемам с полем ++; или другой код, который вводит условия гонки.
Примитивный тип Boolean является атомарным для операций записи и чтения, а volatile гарантирует принцип «происходит раньше». Так что если вам нужны простые get () и set (), вам не нужен AtomicBoolean.
С другой стороны, если вам нужно реализовать некоторую проверку перед установкой значения переменной, например, «если истина, а затем установить на ложь», то вам нужно выполнить эту операцию также атомарно, в этом случае используйте compareAndSet и другие методы, предоставляемые AtomicBoolean, поскольку, если вы попытаетесь реализовать эту логику с помощью volatile boolean, вам потребуется некоторая синхронизация, чтобы убедиться, что значение не изменилось между get и set.
Короткий, хрустящий и по существу. volatileработает только в тех случаях, когда поток владельца может обновлять значение поля, а другие потоки могут только читать.
Chaklader Asfak Arefe
3
Если у вас есть только один поток, модифицирующий ваш логический тип, вы можете использовать логический тип volatile (обычно вы делаете это для определения stopпеременной, проверяемой в основном цикле потока).
Однако, если у вас есть несколько потоков, модифицирующих логическое значение, вы должны использовать AtomicBoolean. Иначе, следующий код не является безопасным:
boolean r =!myVolatileBoolean;
Эта операция выполняется в два этапа:
Логическое значение читается.
Логическое значение записывается.
Если другой поток изменяет значение между #1и 2#, вы можете получить неверный результат. AtomicBooleanметоды избежать этой проблемы, делая шаги #1и #2атомарно.
«Если у вас есть только один поток, изменяющий ваш логический тип, вы можете использовать логическое значение volatile.» Если вы используете один поток, зачем вам нужен volatile (?) .. Вы должны удалить первый абзац, чтобы улучшить ответ ..
minsk
-1
Оба имеют одну и ту же концепцию, но в атомарном логическом значении это обеспечит атомарность операции в случае переключения процессора между ними.
Ответы:
Они просто совершенно разные. Рассмотрим пример целого
volatile
числа:Если два потока вызывают функцию одновременно,
i
может быть 5 впоследствии, так как скомпилированный код будет чем-то похожим на это (за исключением того, что вы не можете выполнить синхронизациюint
):Если переменная является изменчивой, каждый атомарный доступ к ней синхронизируется, но не всегда очевидно, что на самом деле квалифицируется как атомарный доступ. С
Atomic*
объектом гарантируется, что каждый метод является «атомарным».Таким образом, если вы используете
AtomicInteger
иgetAndAdd(int delta)
, вы можете быть уверены, что результат будет10
. Таким же образом, если два потока одновременно отменяютboolean
переменную одновременно, с помощью параметраAtomicBoolean
вы можете быть уверены, что впоследствии оно будет иметь исходное значение, а с помощьюvolatile boolean
- вы не сможете.Поэтому, когда у вас есть несколько потоков, модифицирующих поле, вам нужно сделать его атомарным или использовать явную синхронизацию.
Цель
volatile
другая. Рассмотрим этот примерЕсли у вас есть работающий поток
loop()
и другой поток, вызывающийstop()
, вы можете столкнуться с бесконечным циклом, если пропустите егоvolatile
, поскольку первый поток может кэшировать значение stop. Здесь этоvolatile
служит подсказкой компилятору, чтобы он был немного осторожнее с оптимизацией.источник
volatile
. Вопрос оvolatile boolean
противAtomicBoolean
.volatile boolean
достаточно. Если есть также много авторов, вам может понадобитьсяAtomicBoolean
.Я использую изменчивые поля, когда упомянутое поле ТОЛЬКО ОБНОВЛЯЕТСЯ потоком его владельца, а значение читается только другими потоками, вы можете рассматривать его как сценарий публикации / подписки, в котором есть много наблюдателей, но только один издатель. Однако, если эти наблюдатели должны выполнить некоторую логику, основанную на значении поля, а затем отодвинуть новое значение, тогда я использую Atomic * переменные или блокировки или синхронизированные блоки, что мне больше подходит. Во многих параллельных сценариях он сводится к получению значения, сравнению его с другим и обновлению, если необходимо, следовательно, методы CompareAndSet и getAndSet присутствуют в классах Atomic *.
Проверьте JavaDocs пакета java.util.concurrent.atomic для получения списка классов Atomic и отличного объяснения того, как они работают (только что узнали, что они свободны от блокировок, поэтому они имеют преимущество перед блокировками или синхронизированными блоками)
источник
boolean
var, мы должны выбратьvolatile boolean
.Вы не можете сделать это
compareAndSet
,getAndSet
как атомарную операцию с volatile boolean (если, конечно, вы не синхронизируете ее).источник
AtomicBoolean
имеет методы, которые выполняют свои составные операции атомарно и без использованияsynchronized
блока. С другой стороны,volatile boolean
может выполнять сложные операции, только если это делается внутриsynchronized
блока.Эффекты памяти чтения / записи , чтобы
volatile boolean
идентичныget
иset
методовAtomicBoolean
соответственно.Например,
compareAndSet
метод будет атомарно выполнять следующее (безsynchronized
блока):Следовательно,
compareAndSet
метод позволит вам написать код, который гарантированно будет выполняться только один раз, даже если он вызывается из нескольких потоков. Например:Гарантируется, что он будет уведомлять слушателя только один раз (при условии, что ни один другой поток не установит
AtomicBoolean
егоfalse
снова после того, как он будет установленtrue
).источник
volatile
Ключевое слово гарантирует связь между потоками, разделяющими эту переменную. Это не гарантирует, что 2 или более потоков не будут прерывать друг друга при доступе к этой логической переменной.источник
Atomic*
Класс обертываниеvolatile
поля.Классы Atomic * обертывают летучий примитив того же типа. Из источника:
Так что, если все, что вы делаете, это получаете и настраиваете Atomic *, то вместо этого у вас может быть просто изменчивое поле.
Атомные * классы предоставляют методы , которые обеспечивают более расширенные функциональные возможности, такие как
incrementAndGet()
,compareAndSet()
и другие , которые реализуют несколько операций (GET / инкремент / комплект, тест / набора) без блокировки. Вот почему классы Atomic * такие мощные.Например, если несколько потоков используют следующий код с использованием
++
, будут условия состязания, потому что++
на самом деле: get, increment и set.Однако следующий код будет безопасно работать в многопоточной среде без блокировок:
Также важно отметить, что перенос вашего изменчивого поля с использованием класса Atomic * является хорошим способом инкапсуляции критического общего ресурса с точки зрения объекта. Это означает, что разработчики не могут просто иметь дело с полем, предполагая, что оно не является общим, что может привести к проблемам с полем ++; или другой код, который вводит условия гонки.
источник
Если есть несколько потоков, обращающихся к переменной класса, тогда каждый поток может сохранить копию этой переменной в своем локальном кеше.
Если сделать переменную volatile, потоки не смогут сохранить копию переменной в локальном кеше.
Атомные переменные различны, и они допускают атомное изменение их значений.
источник
Примитивный тип Boolean является атомарным для операций записи и чтения, а volatile гарантирует принцип «происходит раньше». Так что если вам нужны простые get () и set (), вам не нужен AtomicBoolean.
С другой стороны, если вам нужно реализовать некоторую проверку перед установкой значения переменной, например, «если истина, а затем установить на ложь», то вам нужно выполнить эту операцию также атомарно, в этом случае используйте compareAndSet и другие методы, предоставляемые AtomicBoolean, поскольку, если вы попытаетесь реализовать эту логику с помощью volatile boolean, вам потребуется некоторая синхронизация, чтобы убедиться, что значение не изменилось между get и set.
источник
Помните ИДИОМ -
ЧИТАЙТЕ - ИЗМЕНИТЕ - НАПИШИТЕ это, чего вы не можете достичь с помощью volatile
источник
volatile
работает только в тех случаях, когда поток владельца может обновлять значение поля, а другие потоки могут только читать.Если у вас есть только один поток, модифицирующий ваш логический тип, вы можете использовать логический тип volatile (обычно вы делаете это для определения
stop
переменной, проверяемой в основном цикле потока).Однако, если у вас есть несколько потоков, модифицирующих логическое значение, вы должны использовать
AtomicBoolean
. Иначе, следующий код не является безопасным:Эта операция выполняется в два этапа:
Если другой поток изменяет значение между
#1
и2#
, вы можете получить неверный результат.AtomicBoolean
методы избежать этой проблемы, делая шаги#1
и#2
атомарно.источник
Оба имеют одну и ту же концепцию, но в атомарном логическом значении это обеспечит атомарность операции в случае переключения процессора между ними.
источник