Volátil booleano vs AtomicBoolean

O que o AtomicBoolean faz que um booleano volátil não pode alcançar?

Eles são totalmente diferentes. Considere este exemplo de um inteiro volatile :

 volatile int i = 0; void incIBy5() { i += 5; } 

Se dois threads chamarem a function simultaneamente, i poderia ser 5 depois, já que o código compilado será um pouco semelhante a isso (exceto que você não pode sincronizar em int ):

 void incIBy5() { int temp; synchronized(i) { temp = i } synchronized(i) { i = temp + 5 } } 

Se uma variável é volátil, todo access atômico a ela é sincronizado, mas nem sempre é óbvio o que realmente se qualifica como um access atômico. Com um object Atomic* , é garantido que cada método é “atômico”.

Assim, se você usar um AtomicInteger e getAndAdd(int delta) , você pode ter certeza que o resultado será 10 . Da mesma forma, se dois threads AtomicBoolean uma variável boolean simultaneamente, com um AtomicBoolean você pode ter certeza de que possui o valor original depois, com um volatile boolean , você não pode.

Portanto, sempre que você tiver mais de um encadeamento modificando um campo, precisará torná-lo atômico ou usar a synchronization explícita.

O objective do volatile é diferente. Considere este exemplo

 volatile boolean stop = false; void loop() { while (!stop) { ... } } void stop() { stop = true; } 

Se você tiver um thread executando loop() e outro thread chamando stop() , você pode executar um loop infinito se omitir volatile , já que o primeiro thread pode armazenar em cache o valor de stop. Aqui, o volatile serve como uma sugestão para o compilador ser um pouco mais cuidadoso com as otimizações.

Eu uso campos voláteis quando o campo é APENAS ATUALIZADO por seu thread de proprietário e o valor é lido apenas por outros threads, você pode pensar nele como um cenário de publicação / assinatura em que há muitos observadores, mas apenas um editor. No entanto, se esses observadores devem executar alguma lógica com base no valor do campo e, em seguida, empurrar de volta um novo valor, então eu vou com Atomic * vars ou bloqueios ou blocos sincronizados, o que melhor me convier. Em muitos cenários simultâneos, resume-se a obter o valor, compará-lo com outro e atualizar, se necessário, portanto, os methods compareAndSet e getAndSet presentes nas classs Atomic *.

Verifique os JavaDocs do pacote java.util.concurrent.atomic para obter uma lista de classs Atomic e uma excelente explicação de como eles funcionam (apenas aprendemos que eles são livres de bloqueio, para que eles tenham uma vantagem sobre bloqueios ou blocos sincronizados)

Você não pode compareAndSet , getAndSet como uma operação atômica com um booleano volátil (a menos que você o sincronize).

AtomicBoolean possui methods que executam suas operações compostas atomicamente e sem ter que usar um bloco synchronized . Por outro lado, o volatile boolean só pode executar operações compostas se isso for feito dentro de um bloco synchronized .

Os efeitos de memory de leitura / escrita em volatile boolean são idênticos aos methods get e set do AtomicBoolean respectivamente.

Por exemplo, o método compareAndSet irá atomicamente executar o seguinte (sem um bloco synchronized ):

 if (value == expectedValue) { value = newValue; return true; } else { return false; } 

Portanto, o método compareAndSet permitirá que você escreva o código que é garantido para executar apenas uma vez, mesmo quando chamado de vários segmentos. Por exemplo:

 final AtomicBoolean isJobDone = new AtomicBoolean(false); ... if (isJobDone.compareAndSet(false, true)) { listener.notifyJobDone(); } 

É garantido para notificar o ouvinte apenas uma vez (supondo que nenhum outro thread define o AtomicBoolean volta para false novamente depois de ser definido como true ).

garantia de palavra-chave volatile acontece antes do relacionamento entre os threads que compartilham essa variável. Não garante que 2 ou mais threads não irão interromper um ao outro enquanto acessam a variável booleana.

Se houver vários encadeamentos acessando a variável de nível de class, cada encadeamento poderá manter uma cópia dessa variável em seu cache threadlocal.

Tornar a variável volátil evitará que os threads mantenham a cópia da variável no cache threadlocal.

Variáveis ​​atômicas são diferentes e permitem modificação atômica de seus valores.

O tipo primitivo booleano é atômico para operações de gravação e leitura, o volátil garante o princípio acontece antes. Então, se você precisa de um simples get () e set (), então você não precisa do AtomicBoolean.

Por outro lado, se você precisar implementar alguma verificação antes de definir o valor de uma variável, por exemplo, “se true, defina como false”, você precisará executar essa operação atomicamente também, neste caso, use compareAndSet e outros methods fornecidos por AtomicBoolean, já que se você tentar implementar essa lógica com boolean volátil, precisará de alguma synchronization para ter certeza de que o valor não mudou entre get e set.

Volátil booleano vs AtomicBoolean

As classs Atomic * envolvem um primitivo volátil do mesmo tipo. Da fonte:

 public class AtomicLong extends Number implements java.io.Serializable { ... private volatile long value; ... public final long get() { return value; } ... public final void set(long newValue) { value = newValue; } 

Então, se tudo o que você está fazendo é obter e definir um Atomic *, então você pode também ter um campo volátil.

O que o AtomicBoolean faz que um booleano volátil não pode alcançar?

O que as classs Atomic * oferecem, no entanto, são methods que fornecem funcionalidades mais avançadas como incrementAndGet() , compareAndSet() e outras que implementam múltiplas operações (get / increment / set, test / set) sem bloquear. É por isso que as classs Atomic * são tão poderosas.

Por exemplo, se vários segmentos estiverem usando o código a seguir usando ++ , haverá condições de corrida porque ++ é, na verdade: get, increment e set.

 private volatile value; ... // race conditions here value++; 

No entanto, o código a seguir funcionará em um ambiente multi-threaded com segurança:

 private final AtomicLong value = new AtomicLong(); ... value.incrementAndGet(); 

Também é importante observar que agrupar seu campo volátil usando a class Atomic * é uma boa maneira de encapsular o recurso compartilhado crítico do ponto de vista do object. Isso significa que os desenvolvedores não podem simplesmente lidar com o campo, supondo que ele não seja compartilhado, possivelmente injetando problemas com um campo ++; ou outro código que introduz condições de corrida.

Lembre-se do IDIOM –

LEIA – MODIFICAR – ESCREVA isso que você não pode alcançar com a volatilidade

Se você tem apenas um thread modificando seu booleano, você pode usar um booleano volátil (geralmente você faz isso para definir uma variável de stop marcada no loop principal do thread).

No entanto, se você tiver vários segmentos modificando o booleano, deverá usar um AtomicBoolean . Senão, o seguinte código não é seguro:

 boolean r = !myVolatileBoolean; 

Esta operação é feita em duas etapas:

  1. O valor booleano é lido.
  2. O valor booleano é escrito.

Se um outro segmento modificar o valor entre #1 e 2# , você poderá obter um resultado incorreto. AtomicBoolean methods AtomicBoolean evitam esse problema executando as etapas #1 e #2 atomicamente.

Intereting Posts