Quando usamos AtomicReference
?
É necessário criar objetos em todos os programas multithread?
Forneça um exemplo simples em que AtomicReference deve ser usado.
fonte
Quando usamos AtomicReference
?
É necessário criar objetos em todos os programas multithread?
Forneça um exemplo simples em que AtomicReference deve ser usado.
A referência atômica deve ser usada em uma configuração na qual você precisa executar operações atômicas simples (por exemplo, com segurança de thread , não triviais) em uma referência, para a qual a sincronização baseada em monitor não é apropriada. Suponha que você queira verificar se um campo específico apenas se o estado do objeto permanece como você verificou pela última vez:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
Por causa da semântica de referência atômica, você pode fazer isso mesmo se o cache
objeto for compartilhado entre encadeamentos, sem usar synchronized
. Em geral, é melhor usar sincronizadores ou a java.util.concurrent
estrutura, em vez de usar a Atomic*
menos que saiba o que está fazendo.
Duas excelentes referências de árvores mortas que apresentarão esse tópico:
Observe que (não sei se isso sempre foi verdade) a atribuição de referência (por exemplo =
) é atômica (atualizando tipos primitivos de 64 bits como long
ou double
pode não ser atômica; mas a atualização de uma referência é sempre atômica, mesmo que seja de 64 bits ) sem usar explicitamente um Atomic*
.
Consulte a Especificação de linguagem Java 3ed, Seção 17.7 .
AtomicReference
, marque a variávelvolatile
porque, embora o tempo de execução garanta que a atribuição de referência seja atômica, o compilador pode executar otimizações sob o pressuposto de que a variável não estava sendo modificada por outros threads.AtomicReference
"; se você estiver usando, meu conselho seria ir na direção oposta e marcá-lofinal
para que o compilador possa otimizar adequadamente.Uma referência atômica é ideal para usar quando você precisa compartilhar e alterar o estado de um objeto imutável entre vários encadeamentos. Essa é uma afirmação super densa, então vou descrevê-la um pouco.
Primeiro, um objeto imutável é um objeto que não é efetivamente alterado após a construção. Freqüentemente, os métodos de um objeto imutável retornam novas instâncias dessa mesma classe. Alguns exemplos incluem as classes de wrapper Long e Double, bem como String, apenas para citar alguns. (De acordo com a Programação de simultaneidade nos objetos imutáveis da JVM , é uma parte crítica da simultaneidade moderna).
A seguir, por que o AtomicReference é melhor que um objeto volátil para compartilhar esse valor compartilhado. Um exemplo de código simples mostrará a diferença.
Toda vez que você deseja modificar a sequência referenciada por esse campo volátil com base em seu valor atual, primeiro você precisa obter um bloqueio nesse objeto. Isso impede que algum outro encadeamento entre nesse meio tempo e altere o valor no meio da nova concatenação de strings. Então, quando o seu encadeamento continuar, você derruba o trabalho do outro encadeamento. Mas, honestamente, esse código funcionará, parece limpo e faria a maioria das pessoas feliz.
Pequeno problema. Está lento. Especialmente se houver muita contenção nesse objeto de bloqueio. Isso ocorre porque a maioria dos bloqueios exige uma chamada do sistema OS, e seu thread bloqueará e será alternado para fora da CPU para abrir caminho para outros processos.
A outra opção é usar um AtomicRefrence.
Agora, por que isso é melhor? Honestamente, esse código é um pouco menos limpo do que antes. Mas há algo realmente importante que acontece no AtomicRefrence, que é comparar e trocar. É uma única instrução da CPU, não uma chamada do SO, que faz a troca acontecer. Essa é uma única instrução na CPU. E como não há bloqueios, não há mudança de contexto no caso em que o bloqueio é exercido, o que economiza ainda mais tempo!
O problema é que, para AtomicReferences, isso não usa uma chamada .equals (), mas uma comparação == para o valor esperado. Portanto, verifique se o esperado é o objeto real retornado de entrar no loop.
fonte
worked
para obter a mesma semântica.Aqui está um caso de uso para AtomicReference:
Considere esta classe que atua como um intervalo numérico e usa variáveis AtmomicInteger individuais para manter limites numéricos inferiores e superiores.
Ambos setLower e setUpper são sequências de verificação e ação, mas não usam bloqueio suficiente para torná-las atômicas. Se o intervalo de números for mantido (0, 10) e um thread chamar setLower (5) enquanto outro thread chamar setUpper (4), com algum tempo de azar, ambos passarão nas verificações nos setters e as duas modificações serão aplicadas. O resultado é que o intervalo agora mantém (5, 4) um estado inválido. Portanto, enquanto os AtomicIntegers subjacentes são seguros para threads, a classe composta não é. Isso pode ser corrigido usando um AtomicReference em vez de usar AtomicIntegers individuais para os limites superior e inferior.
fonte
Você pode usar AtomicReference ao aplicar bloqueios otimistas. Você tem um objeto compartilhado e deseja alterá-lo de mais de 1 thread.
Como outro segmento pode ter modificado e / pode modificar entre essas duas etapas. Você precisa fazer isso em uma operação atômica. é aqui que o AtomicReference pode ajudar
fonte
Aqui está um caso de uso muito simples e não tem nada a ver com segurança de encadeamento.
Para compartilhar um objeto entre invocações lambda,
AtomicReference
é uma opção :Não estou dizendo que isso seja um bom design ou algo assim (é apenas um exemplo trivial), mas se você tiver o caso em que precisa compartilhar um objeto entre invocações lambda, essa
AtomicReference
é uma opção.De fato, você pode usar qualquer objeto que contenha uma referência, mesmo uma coleção que tenha apenas um item. No entanto, o AtomicReference é um ajuste perfeito.
fonte
Eu não vou falar muito. Meus respeitados colegas já deram sua contribuição valiosa. O código de execução completo no final deste blog deve remover qualquer confusão. Trata-se de um assento de filme que reserva um pequeno programa no cenário multiencadeado.
Alguns fatos elementares importantes são os seguintes. 1> Diferentes threads podem apenas conter, por exemplo, variáveis de membro estáticas no espaço de heap. 2> A leitura ou gravação volátil são completamente atômicas e serializadas / acontecem antes e são feitas apenas a partir da memória. Ao dizer isso, quero dizer que qualquer leitura seguirá a gravação anterior na memória. E qualquer gravação seguirá a leitura anterior da memória. Portanto, qualquer encadeamento que trabalhe com um volátil sempre verá o valor mais atualizado. AtomicReference usa essa propriedade de volátil.
A seguir, estão alguns dos códigos-fonte do AtomicReference. AtomicReference refere-se a uma referência de objeto. Essa referência é uma variável de membro volátil na instância AtomicReference, como abaixo.
get () simplesmente retorna o valor mais recente da variável (como os voláteis fazem da maneira "acontece antes").
A seguir, é apresentado o método mais importante do AtomicReference.
O método compareAndSet (expect, update) chama o método compareAndSwapObject () da classe insegura de Java. Essa chamada de método inseguro chama a chamada nativa, que chama uma única instrução para o processador. "expect" e "update" referenciam cada objeto.
Se e somente se a variável de membro da instância AtomicReference "value" se referir ao mesmo objeto for referida por "expect", "update" será atribuída a essa variável de instância agora e "true" será retornado. Ou então, false é retornado. A coisa toda é feita atomicamente. Nenhum outro segmento pode interceptar no meio. Como esta é uma operação de processador único (mágica da arquitetura moderna do computador), geralmente é mais rápida do que usar um bloco sincronizado. Mas lembre-se de que quando várias variáveis precisam ser atualizadas atomicamente, o AtomicReference não ajuda.
Gostaria de adicionar um código de execução completo, que pode ser executado no eclipse. Isso esclareceria muitas confusões. Aqui 22 usuários (threads MyTh) estão tentando reservar 20 lugares. A seguir está o trecho de código seguido pelo código completo.
Snippet de código em que 22 usuários estão tentando reservar 20 vagas.
A seguir, é apresentado o código completo.
fonte
AtomicReference é uma maneira flexível de atualizar o valor da variável atomicamente sem o uso de sincronização.
AtomicReference
suporte a programação segura de thread sem bloqueio em variáveis únicas.Existem várias maneiras de alcançar a segurança do thread com API simultânea de alto nível . Variáveis atômicas são uma das várias opções.
Lock
objetos oferecem suporte a idiomas de bloqueio que simplificam muitos aplicativos simultâneos.Executors
defina uma API de alto nível para iniciar e gerenciar threads. As implementações de executores fornecidas pelo java.util.concurrent fornecem gerenciamento de conjunto de encadeamentos adequado para aplicativos de grande escala.Coleções simultâneas facilitam o gerenciamento de grandes coleções de dados e podem reduzir bastante a necessidade de sincronização.
As variáveis atômicas possuem recursos que minimizam a sincronização e ajudam a evitar erros de consistência de memória.
Código de exemplo com
AtomicReference
:Você não precisa usar
AtomicReference
em todos os programas multiencadeados.Se você deseja proteger uma única variável, use
AtomicReference
. Se você deseja proteger um bloco de código, use outras construções comoLock
/synchronized
etc.fonte
Outro exemplo simples é fazer uma modificação de thread seguro em um objeto de sessão.
Fonte: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
fonte