Изменчивая ссылка Java против AtomicReference

135

Есть ли какая-то разница между volatileссылкой на объект и AtomicReferenceв случае, если бы я просто использовал get()и set()-методы из AtomicReference?

Auramo
источник

Ответы:

114

Краткий ответ: Нет.

Из java.util.concurrent.atomicпакетной документации. Цитировать:

Эффекты памяти для доступа и обновления атомных элементов обычно следуют правилам для летучих компонентов:

  • getимеет эффекты памяти чтения volatileпеременной.
  • setимеет эффект памяти записи (присваивания) volatileпеременной.

Кстати, эта документация очень хорошая, и все объясняется.


AtomicReference::lazySetпредставляет собой более новую (Java 6+) операцию, семантика которой недостижима через volatileпеременные. Смотрите этот пост для получения дополнительной информации.

pgras
источник
11
И чем дольше будет ответ?
Жюльен Гренье
Согласовано. Нам хоть ссылка нужна.
Жюльен Частанг,
2
Ссылка для более длинного ответа: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/…
Алекс Симан
43

Нет, нет

Дополнительные возможности, предоставляемые AtomicReference, - это метод compareAndSet () и друзья. Если вам не нужны эти методы, переменная ссылка предоставляет ту же семантику, что и AtomicReference.set () и .get ().

Avi
источник
14

Есть несколько отличий и компромиссов:

  1. Использование AtomicReferenceget / set имеет ту же семантику JMM, что и энергозависимое поле (как состояния javadoc), но AtomicReferenceявляется оберткой вокруг ссылки, поэтому любой доступ к полю включает дальнейшую погоню за указателем .

  2. Объем памяти увеличивается (при условии сжатой среды ООП, что справедливо для большинства виртуальных машин):

    • изменчивый ref = 4b
    • AtomicReference = 4b + 16b (заголовок объекта 12b + поле ссылки 4b)
  3. AtomicReferenceпредлагает более богатый API, чем нестабильная ссылка. Вы можете восстановить API для энергозависимой ссылки с помощью AtomicFieldUpdaterили с помощью Java 9 a VarHandle. Вы также можете связаться прямо, sun.misc.Unsafeесли вам нравится бегать с ножницами. AtomicReferenceСам реализован с помощью Unsafe.

Итак, когда это хорошо, чтобы выбрать один над другим:

  • Нужно только получить / установить? Придерживайтесь изменчивого поля, простейшего решения и минимальных накладных расходов.
  • Нужна дополнительная функциональность? Если это часть кода, чувствительная к производительности (скорости / накладным расходам памяти), сделайте выбор между AtomicReference/ AtomicFieldUpdater/, Unsafeгде вы склонны платить за удобочитаемость и риск увеличения производительности. Если это не чувствительная область просто пойти AtomicReference. Авторы библиотек обычно используют сочетание этих методов в зависимости от целевых JDK, ожидаемых ограничений API, ограничений памяти и так далее.
Ницан Вакарт
источник
7

Исходный код JDK - один из лучших способов решить такую ​​проблему. Если вы посмотрите на код в AtomicReference, он использует переменную volatie для хранения объектов.

private volatile V value;

Итак, очевидно, что если вы собираетесь просто использовать get () и set () в AtomicReference, это похоже на использование переменной volatile. Но, как отметили другие читатели, AtomicReference предоставляет дополнительную семантику CAS. Итак, сначала решите, хотите ли вы семантику CAS или нет, и если вы делаете только тогда, используйте AtomicReference.

бесконечный
источник
13
«Исходный код JDK - один из лучших способов ответить на подобную путаницу» => Я не обязательно согласен - javadoc (который является контрактом класса) - лучший способ. То, что вы найдете в коде, отвечает на вопрос для конкретной реализации, но код может измениться.
assylias
4
Например, эта переменная в hashmap была энергозависимой в JDK 6, но больше не является энергозависимой в Java 7. Если бы вы основали свой код на том факте, что переменная была энергозависимой, она бы сломалась при ухудшении качества JDK ... разные, но вы поняли.
assylias
Это CAS стандартная аббревиатура?
аббас
1
Сравните и
бесконечные
4

AtomicReferenceобеспечивает дополнительную функциональность, которую не обеспечивает обычная переменная переменная. Когда вы прочитали API Javadoc, вы это узнаете, но он также предоставляет блокировку, которая может быть полезна для некоторых операций.

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

Питер Лори
источник
Итак, разница заключается в их производительности. Если бы не было никакой разницы, вы бы никогда не предложили использовать один поверх другого.
BT
Производительность почти такая же. AtomicRefrence добавляет сложность и использование памяти.
Питер Лори
@BT volatileПоле можно использовать как любое обычное поле, тогда как для доступа к значению AtomicReferenceтребуется пройти через методы getи setметоды.
Дэвид Харкнесс
0

Иногда, даже если вы используете только get и set, AtomicReference может быть хорошим выбором:

Пример с volatile:

private volatile Status status;
...
public setNewStatus(Status newStatus){
  status = newStatus;
}

public void doSomethingConditionally() {
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
  }
}

Реализация с AtomicReference обеспечит вам бесплатную синхронизацию копирования при записи.

private AtomicReference<Status> statusWrapper;
...

public void doSomethingConditionally() {
  Status status = statusWrapper.get();
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
  }
}

Можно сказать, что у вас все еще может быть правильная копия, если вы замените:

Status status = statusWrapper.get();

с участием:

Status statusCopy = status;

Однако второй, скорее всего, будет случайно удален кем-то в будущем во время «очистки кода».

NME
источник