Respostas:
A resposta curta é: Não.
Na java.util.concurrent.atomic
documentação do pacote. Citar:
Os efeitos de memória para acessos e atualizações de atômicas geralmente seguem as regras para voláteis:
get
tem os efeitos de memória da leitura de umavolatile
variável.set
tem os efeitos de memória de escrever (atribuir) umavolatile
variável.
A propósito, essa documentação é muito boa e tudo é explicado.
AtomicReference::lazySet
é uma operação mais recente (Java 6+) introduzida que possui semântica inatingível por meio de volatile
variáveis. Veja este post para mais informações.
Existem várias diferenças e trocas:
O uso de um AtomicReference
get / set tem a mesma semântica JMM que um campo volátil (como o javadoc afirma), mas AtomicReference
é um invólucro em torno de uma referência, portanto, qualquer acesso ao campo envolve uma busca adicional por ponteiro .
O espaço ocupado pela memória é multiplicado (assumindo um ambiente de OOPs compactado, o que é verdadeiro para a maioria das VMs):
AtomicReference
= 4b + 16b (cabeçalho do objeto 12b + campo de ref 4b)AtomicReference
oferece uma API mais rica que uma referência volátil. Você pode recuperar a API para a referência volátil usando um AtomicFieldUpdater
, ou com Java 9 a VarHandle
. Você também pode alcançar diretamente sun.misc.Unsafe
se gosta de correr com uma tesoura. AtomicReference
em si é implementado usando Unsafe
.
Então, quando é bom escolher um sobre o outro:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
onde você tende a pagar em legibilidade e risco pelo ganho de desempenho. Se esta não é uma área sensível, basta procurar AtomicReference
. Os criadores de bibliotecas geralmente usam uma combinação desses métodos, dependendo dos JDKs direcionados, das restrições esperadas da API, das restrições de memória e assim por diante.O código fonte do JDK é uma das melhores maneiras de responder a confusões como essa. Se você observar o código no AtomicReference, ele usará uma variável volatie para armazenamento de objetos.
private volatile V value;
Então, obviamente, se você vai usar apenas get () e set () no AtomicReference, é como usar uma variável volátil. Mas, como outros leitores comentaram, o AtomicReference fornece semântica adicional do CAS. Portanto, primeiro decida se deseja ou não a semântica do CAS e, se desejar, use o AtomicReference.
AtomicReference
fornece funcionalidade adicional que uma variável volátil simples não fornece. Ao ler a Javadoc da API, você saberá disso, mas também fornece um bloqueio que pode ser útil para algumas operações.
No entanto, a menos que você precise dessa funcionalidade adicional, sugiro que você use um volatile
campo simples .
volatile
campo pode ser usado como qualquer campo regular, enquanto acessar o valor em uma AtomicReference
requer passando get
e set
métodos.
Às vezes, mesmo se você usar apenas get e sets, AtomicReference pode ser uma boa opção:
Exemplo com volátil:
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
}
}
A implementação com o AtomicReference forneceria uma sincronização de cópia na gravação gratuitamente.
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
}
}
Pode-se dizer que você ainda poderia ter uma cópia adequada se substituísse:
Status status = statusWrapper.get();
com:
Status statusCopy = status;
No entanto, é mais provável que o segundo seja removido por alguém acidentalmente no futuro durante a "limpeza de código".