Когда предпочтительнее использовать 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, то для него это вполне нормально использовать. Фактически, это одно из немногих разумных применений для летучих.