Летучий логический против AtomicBoolean - PullRequest
211 голосов
/ 24 сентября 2010

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

Ответы [ 10 ]

241 голосов
/ 24 сентября 2010

Я использую изменчивые поля, когда указанное поле ТОЛЬКО ОБНОВЛЯЕТСЯ его потоком-владельцем, а значение читается только другими потоками, вы можете рассматривать его как сценарий публикации / подписки, где есть много наблюдателей, но только один издатель.Однако, если эти наблюдатели должны выполнить некоторую логику, основанную на значении поля, а затем отодвинуть новое значение, тогда я использую Atomic * переменные или блокировки или синхронизированные блоки, все, что мне подходит.Во многих параллельных сценариях он сводится к получению значения, сравнению его с другим и обновлению при необходимости, следовательно, методы compareAndSet и getAndSet присутствуют в классах Atomic *.

Проверьте JavaDocs пакета java.util.concurrent.atomic для получения списка классов Atomic и превосходного объяснения того, как они работают (только что узнали, что они не блокируются, поэтомуони имеют преимущество перед замками или синхронизированными блоками)

87 голосов
/ 24 сентября 2010

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

volatile int i = 0;
void incIBy5() {
    i += 5;
}

Если два потока вызывают функцию одновременно, i может быть 5 впоследствии, так как скомпилированный код будет чем-то похож на это (за исключением того, что вы не можете синхронизироватьon 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 служит подсказкой компилятору, чтобы он был немного осторожнее с оптимизацией.

51 голосов
/ 24 сентября 2010

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

39 голосов
/ 26 октября 2012

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 голосов
/ 24 сентября 2010

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

5 голосов
/ 06 сентября 2016

Volatile boolean vs 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 / increment / set, тест / комплект) без блокировки.Вот почему классы Atomic * такие мощные.

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

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

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

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

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

5 голосов
/ 27 декабря 2011

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

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

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

4 голосов
/ 18 ноября 2013

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

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

3 голосов
/ 29 сентября 2011

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

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

2 голосов
/ 05 ноября 2015

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

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

boolean r = !myVolatileBoolean;

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

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

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

...