Когда предпочтительнее использовать volatile boolean в Java, а не AtomicBoolean?

Я рассмотрел другие вопросы volatile vs. Atomicxxxx в SO (включая этот) и прочитал описание java.util.current.atomic, и я не совсем доволен нюансами.

Если я пытаюсь решить между использованием volatile boolean и AtomicBoolean, существуют ли практические различия помимо операций атомарного чтения-изменения-записи, предлагаемых AtomicBoolean? (например, compareAndSet() и getAndSet())

Предположим, что

volatile boolean flag;

Затем один или несколько потоков устанавливают флаг (но не очищают его). Если у меня есть один поток, который читает флаг, и если он установлен, выполняет действие, а затем очищает флаг, volatile соответствует?

Существует ли более высокая стоимость AtomicBoolean, чем volatile boolean, с точки зрения

  • объем памяти
  • Достижение производительности (volatile boolean, похоже, требует фехтования памяти, AtomicBoolean, как представляется, требует ограждения памяти + некоторая незначительная блокировка операций CAS в соответствии с описанием java.util.current.atomic)

Мой вызов кишки состоит в том, чтобы просто пойти с AtomicBoolean и быть в безопасности, но я хочу понять, есть ли когда-нибудь ситуация вместо volatile boolean (например, если у меня было тысячи экземпляров их и производительность были проблемой).

Ответ 1

По существу все AtomicBoolean являются volatile boolean в объекте.

На объект будет небольшая накладная. Вероятно, незначительно, но, возможно, больше памяти для доступа в кеш.

Если вам нужно использовать AtomicBooleanFieldUpdater, тогда нагрузка слишком высока. Это может быть хорошо, если вы не собираетесь делать это часто (как attach в NIO).

Ответ 2

Основное отличие между AtomicBoolean и volatile с практической точки зрения состоит в том, что операция сравнения и установки не является атомарной с переменными volatile.

 volatile boolean b;

 void foo() {
   if( b ) {
     //Here another thread might have already changed the value of b to false
     b = false;
   }
 }

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

Ответ 3

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

В простом случае чередование может выглядеть так:

Thread 1 (writer)   Thread 2 (Writer)  Thread 3 (Reader)
-----------------   -----------------  -----------------
flag = true;
                                       if (flag) {
                    flag = true;
                                         flag = false;
                                         doStuff();

и это может быть хорошо (второй набор от flag до true не имеет значения, так как doStuff() будет по-прежнему видеть, что требуется Thread 2.

Однако, если вы отмените поток заказов 3, выполните:

Thread 1 (writer)   Thread 2 (Writer)  Thread 3 (Reader)
-----------------   -----------------  -----------------
flag = true;
                                       if (flag) {
                                         doStuff();
                    flag = true;
                                         flag = false;

тогда обновление Thread 2 может быть потеряно.

Конечно, вы должны быть одинаково осторожны с тем, что еще делает Thread 2, чтобы убедиться, что он виден для Thread 3. Если есть другое состояние, которое требуется задать Thread 2, порядок также становится важным.

В простом случае, да, вы в порядке, но если он становится более сложным, чем простое прокручивание флагов, это становится намного сложнее рассуждать.

Ответ 4

Здесь есть много хорошей информации. Однако я бы добавил еще одну разницу, которая может быть полезна. У вас может быть массив AtomicBooleans, но вы не можете (насколько мне известно) иметь массив volatile booleans.

Ответ 5

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

Да, если вам не нужны специальные возможности AtomicBoolean, то для него это вполне нормально использовать. Фактически, это одно из немногих разумных применений для летучих.