Відповіді:
Коротка відповідь: Ні.
З java.util.concurrent.atomicпакетної документації. Цитувати:
Ефекти пам’яті для доступу та оновлень атома зазвичай відповідають правилам летючих речовин:
getмає ефекти на пам'ять читанняvolatileзмінної.setмає ефекти на пам'ять написання (призначення)volatileзмінної.
До речі, ця документація дуже гарна і все пояснено.
AtomicReference::lazySetце новіша операція (Java 6+), що має семантику, недоступну за допомогою volatileзмінних. Дивіться цю публікацію для отримання додаткової інформації.
Існує кілька відмінностей і компромісів:
Використання AtomicReferenceget / 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;
Однак другий, швидше за все, буде видалений кимось випадково в майбутньому під час "чищення коду".