Летучий логический против AtomicBoolean


245

Что делает AtomicBoolean, чего не может достичь изменчивое логическое значение?


16
Я искал более детальный ответ на вопрос: «Каковы ограничения каждого?». Например, если is - флаг, установленный одним потоком и прочитанный одним или несколькими другими, в AtomicBoolean нет необходимости. Однако, как я вижу из этих ответов, если поток совместно использует переменную в нескольких потоках, которые могут записывать и действуют в результате их чтения, AtomicBoolean вводит в игру операции неблокирования типа CAS. На самом деле, я немного учусь здесь. Надеюсь, другие тоже выиграют.
JeffV


volatile boolean потребуется явная синхронизация для обработки условий гонки, другими словами, сценарий, такой как общий ресурс, обновляется (изменение состояния) несколькими потоками, например, счетчик приращения / уменьшения или переключение логического значения.
Саквояж

Ответы:


98

Они просто совершенно разные. Рассмотрим пример целого volatileчисла:

volatile int 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- вы не сможете.

Поэтому, когда у вас есть несколько потоков, модифицирующих поле, вам нужно сделать его атомарным или использовать явную синхронизацию.

Цель volatileдругая. Рассмотрим этот пример

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

Если у вас есть работающий поток loop()и другой поток, вызывающий stop(), вы можете столкнуться с бесконечным циклом, если пропустите его volatile, поскольку первый поток может кэшировать значение stop. Здесь это volatileслужит подсказкой компилятору, чтобы он был немного осторожнее с оптимизацией.


84
-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
@ksl Я думаю, что @teto хочет описать, что если только один поток изменяет booleanvar, мы должны выбрать volatile boolean.
znlyj

2
Отличное резюме.
Равиндра Бабу

56

Вы не можете сделать это compareAndSet, getAndSetкак атомарную операцию с volatile boolean (если, конечно, вы не синхронизируете ее).


6
Это правда, но не будет ли это довольно редким требованием для логического выражения?
Робин

1
@Robin подумает об использовании его для управления отложенным вызовом метода инициализации.
Устаман Сангат

На самом деле я думаю, что это один из основных вариантов использования.
fool4jesus

42

AtomicBooleanимеет методы, которые выполняют свои составные операции атомарно и без использования synchronizedблока. С другой стороны, volatile booleanможет выполнять сложные операции, только если это делается внутри synchronizedблока.

Эффекты памяти чтения / записи , чтобы volatile booleanидентичны getи setметодов AtomicBooleanсоответственно.

Например, compareAndSetметод будет атомарно выполнять следующее (без synchronizedблока):

if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}

Следовательно, compareAndSetметод позволит вам написать код, который гарантированно будет выполняться только один раз, даже если он вызывается из нескольких потоков. Например:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

if (isJobDone.compareAndSet(false, true)) {
    listener.notifyJobDone();
}

Гарантируется, что он будет уведомлять слушателя только один раз (при условии, что ни один другой поток не установит AtomicBooleanего falseснова после того, как он будет установлен true).


14

volatileКлючевое слово гарантирует связь между потоками, разделяющими эту переменную. Это не гарантирует, что 2 или более потоков не будут прерывать друг друга при доступе к этой логической переменной.


14
Логический (как в примитивном типе) доступ является атомарным в Java. Оба читает и задания. Так что никакой другой поток не будет «прерывать» логические операции.
Maciej Biłas

1
Извините, но как это ответит на вопрос? Atomic*Класс обертывание volatileполя.
Серый

Разве кэши ЦП не являются основным фактором для установки volatile? Чтобы убедиться, что значение на самом деле соответствует тому, которое было установлено в последнее время
jocull

8

Летучий логический против AtomicBoolean

Классы Atomic * обертывают летучий примитив того же типа. Из источника:

public class AtomicLong extends Number implements java.io.Serializable {
   ...
   private volatile long value;
   ...
   public final long get() {
       return value;
   }
   ...
   public final void set(long newValue) {
       value = newValue;
   }

Так что, если все, что вы делаете, это получаете и настраиваете Atomic *, то вместо этого у вас может быть просто изменчивое поле.

Что делает AtomicBoolean, чего не может достичь изменчивое логическое значение?

Атомные * классы предоставляют методы , которые обеспечивают более расширенные функциональные возможности, такие как incrementAndGet(), compareAndSet()и другие , которые реализуют несколько операций (GET / инкремент / комплект, тест / набора) без блокировки. Вот почему классы Atomic * такие мощные.

Например, если несколько потоков используют следующий код с использованием ++, будут условия состязания, потому что ++на самом деле: get, increment и set.

private volatile value;
...
// race conditions here
value++;

Однако следующий код будет безопасно работать в многопоточной среде без блокировок:

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

Также важно отметить, что перенос вашего изменчивого поля с использованием класса Atomic * является хорошим способом инкапсуляции критического общего ресурса с точки зрения объекта. Это означает, что разработчики не могут просто иметь дело с полем, предполагая, что оно не является общим, что может привести к проблемам с полем ++; или другой код, который вводит условия гонки.


5

Если есть несколько потоков, обращающихся к переменной класса, тогда каждый поток может сохранить копию этой переменной в своем локальном кеше.

Если сделать переменную volatile, потоки не смогут сохранить копию переменной в локальном кеше.

Атомные переменные различны, и они допускают атомное изменение их значений.


5

Примитивный тип Boolean является атомарным для операций записи и чтения, а volatile гарантирует принцип «происходит раньше». Так что если вам нужны простые get () и set (), вам не нужен AtomicBoolean.

С другой стороны, если вам нужно реализовать некоторую проверку перед установкой значения переменной, например, «если истина, а затем установить на ложь», то вам нужно выполнить эту операцию также атомарно, в этом случае используйте compareAndSet и другие методы, предоставляемые AtomicBoolean, поскольку, если вы попытаетесь реализовать эту логику с помощью volatile boolean, вам потребуется некоторая синхронизация, чтобы убедиться, что значение не изменилось между get и set.


3

Помните ИДИОМ -

ЧИТАЙТЕ - ИЗМЕНИТЕ - НАПИШИТЕ это, чего вы не можете достичь с помощью volatile


2
Короткий, хрустящий и по существу. volatileработает только в тех случаях, когда поток владельца может обновлять значение поля, а другие потоки могут только читать.
Chaklader Asfak Arefe

3

Если у вас есть только один поток, модифицирующий ваш логический тип, вы можете использовать логический тип volatile (обычно вы делаете это для определения stopпеременной, проверяемой в основном цикле потока).

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

boolean r = !myVolatileBoolean;

Эта операция выполняется в два этапа:

  1. Логическое значение читается.
  2. Логическое значение записывается.

Если другой поток изменяет значение между #1и 2#, вы можете получить неверный результат. AtomicBooleanметоды избежать этой проблемы, делая шаги #1и #2атомарно.


«Если у вас есть только один поток, изменяющий ваш логический тип, вы можете использовать логическое значение volatile.» Если вы используете один поток, зачем вам нужен volatile (?) .. Вы должны удалить первый абзац, чтобы улучшить ответ ..
minsk

-1

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.