В чем разница между использованием летучего примитива над атомными переменными?
Возможный дубликат:
Java: volatile boolean vs AtomicBoolean
Когда целесообразно использовать примитив volatile
(например, boolean
, integer
или long
) вместо AtomicBoolean
, AtomicInteger
или AtomicLong
и наоборот?
Ответы
Ответ 1
Семантика видимости точно такая же, ситуация, когда использование атомных примитивов полезна, - это когда вам нужно использовать свои атомные методы.
Например:
if (volatileBoolean) {
volatileBoolean = !volatileBoolean;
}
может создавать проблемы в многопоточной среде, поскольку переменная может меняться между двумя строками. Если вам нужно, чтобы тест & назначение был атомарным, вы можете использовать:
atomicBoolean.compareAndSet(true, false);
Ответ 2
Использование простой volatile проще и эффективнее, если все, что вы хотите сделать, установлено или получает значение. (Но не получите & установите значение)
Если вам нужно больше операций, таких как getAndIncrement или compareAndSwap, вам нужно использовать классы AtomicXxxx.
Ответ 3
Мы начали запрещать использование volatile
в наших источниках, потому что очень легко писать код, который не всегда работает так, как ожидалось.
По моему опыту, люди добавляют volatile для совместного использования значения между потоками. И тогда кто-то еще начинает изменять значение. И большую часть времени это работает. Но в производстве вы начинаете получать нечетные ошибки, которые действительно трудно отследить. Счетчики увеличиваются в 100 000 раз (тесты только увеличивают их в 10 раз), но заканчиваются на 99'997. В Java 1.4 длинные значения могут быть повреждены действительно, очень редко.
Классы-помощники Atomic*
, с другой стороны, налагают только небольшие накладные расходы, и они всегда работают как рекламируемые.
Поэтому, если у вас есть очень хорошая причина (*) использовать volatile
, всегда предпочитайте вспомогательные классы Atomic*
.
Если вы точно не знаете, что делает каждый символ в классах помощника Atomic*
, тогда вам действительно следует избегать volatile
.
*: преждевременная оптимизация никогда не является веской причиной.
Ответ 4
В классах Atomic*
выполняется перенос примитива volatile
того же типа. Из источника:
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;
}
Итак.
Когда целесообразно использовать летучий примитив (например, логический, целочисленный или длинный) вместо AtomicBoolean, AtomicInteger или AtomicLong
Если все, что вы делаете, это получение и установка Atomic*
, тогда вы можете просто иметь поле volatile
.
... и наоборот?
Тем не менее, существуют классы Atomic*
, которые представляют собой более сложные функции, такие как incrementAndGet()
, compareAndSet()
и другие, которые реализуют несколько операций (get/increment/set, test/set) без блокировки. Вот почему классы Atomic*
настолько мощные.
Также важно отметить, что упаковка поля volatile
с использованием класса Atomic*
- хороший способ инкапсулировать критически важный общий ресурс с точки зрения объекта. Это означает, что разработчики не могут просто иметь дело с полем, предполагая, что он не разделен, возможно, вставляя проблемы с помощью field++;
или другого кода, который вводит условия гонки.