Відповіді:
Коротка відповідь: Ні.
З java.util.concurrent.atomic
пакетної документації. Цитувати:
Ефекти пам’яті для доступу та оновлень атома зазвичай відповідають правилам летючих речовин:
get
має ефекти на пам'ять читанняvolatile
змінної.set
має ефекти на пам'ять написання (призначення)volatile
змінної.
До речі, ця документація дуже гарна і все пояснено.
AtomicReference::lazySet
це новіша операція (Java 6+), що має семантику, недоступну за допомогою volatile
змінних. Дивіться цю публікацію для отримання додаткової інформації.
Існує кілька відмінностей і компромісів:
Використання AtomicReference
get / set має ту саму семантику JMM , що і мінливе поле (як заявляє javadoc), але AtomicReference
є обгорткою навколо посилання, тому будь-який доступ до поля передбачає подальше погоню за покажчиком .
Шлях пам’яті множиться (припускаючи стиснене середовище OOPs, що справедливо для більшості віртуальних машин):
AtomicReference
= 4b + 16b (12b заголовок об’єкта + 4b ref поле)AtomicReference
пропонує більш багатий API, ніж мінливий посилання. Ви можете відновити API для енергонезалежних посилань, використовуючи AtomicFieldUpdater
або за допомогою Java 9 a VarHandle
. Ви також можете досягти прямо, sun.misc.Unsafe
якщо вам подобається бігати ножицями. AtomicReference
сама реалізується за допомогою Unsafe
.
Отже, коли добре вибирати одне над іншим:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
де ви, як правило, платите за читабельність та ризик для підвищення продуктивності. Якщо це не чутлива область, просто зайдіть AtomicReference
. Письменники бібліотеки зазвичай використовують поєднання цих методів залежно від націлених JDK, очікуваних обмежень API, обмежень пам'яті тощо.Вихідний код JDK - це один з найкращих способів відповіді на подібні плутанини. Якщо ви подивитеся на код у AtomicReference, він використовує змінну volatie для зберігання об'єктів.
private volatile V value;
Отже, очевидно, якщо ви збираєтеся просто використовувати get () і set () на AtomicReference, це як використання летючої змінної. Але, як коментують інші читачі, AtomicReference надає додаткову семантику CAS. Отже, спочатку вирішіть, хочете ви семантику CAS чи ні, а якщо ви робите лише тоді, використовуйте AtomicReference.
AtomicReference
надає додаткову функціональність, яку звичайна мінлива зміна не забезпечує. Коли ви прочитали API Javadoc, ви дізнаєтесь це, але він також забезпечує блокування, яке може бути корисним для деяких операцій.
Однак, якщо вам не потрібна ця додаткова функціональність, я пропоную використовувати звичайне volatile
поле.
volatile
Поле може бути використане як і будь-яке звичайне поле, тоді як доступ до значення у AtomicReference
необхідному проходженні get
та set
методах.
Іноді, навіть якщо ви використовуєте лише get і sets, AtomicReference може бути хорошим вибором:
Приклад з летючими:
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;
Однак другий, швидше за все, буде видалений кимось випадково в майбутньому під час "чищення коду".