Eu estava procurando uma resposta mais sutil para: "quais são as limitações de cada uma?". Por exemplo, se is é um sinalizador definido por um segmento e lido por um ou mais outros, não há necessidade de AtomicBoolean. No entanto, como estou vendo com essas respostas, se o thread compartilha uma variável em vários threads pode escrever e está atuando no resultado de suas leituras, o AtomicBoolean coloca em operação as operações sem travamento do tipo CAS. Estou aprendendo bastante aqui, na verdade. Felizmente, outros se beneficiarão também.
booleano volátil precisará de sincronização explícita para lidar com condições de corrida, em outras palavras, cenário como recurso compartilhado sendo atualizado (mudança de estado) por vários threads, por exemplo, contador de incremento / decremento ou inversão de booleano.
Eles são totalmente diferentes. Considere este exemplo de um volatilenúmero inteiro:
volatileint i =0;void incIBy5(){
i +=5;}
Se dois threads chamam a função simultaneamente, ipode ser 5 depois, pois o código compilado será um pouco semelhante a este (exceto que você não pode sincronizar int):
void incIBy5(){int temp;synchronized(i){ temp = i }synchronized(i){ i = temp +5}}
Se uma variável é volátil, todo acesso atômico a ela é sincronizado, mas nem sempre é óbvio o que realmente se qualifica como acesso atômico. Com um Atomic*objeto, é garantido que todo método é "atômico".
Assim, se você usar um AtomicIntegere getAndAdd(int delta), pode ter certeza de que o resultado será 10. Da mesma forma, se dois threads negam uma booleanvariável simultaneamente, com um AtomicBooleanvocê pode ter certeza de que possui o valor original posteriormente, com um volatile boolean, você não pode.
Portanto, sempre que houver mais de um encadeamento modificando um campo, você precisará torná-lo atômico ou usar a sincronização explícita.
O objetivo de volatileé diferente. Considere este exemplo
Se você tiver um thread em execução loop()e outro chamado de thread stop(), poderá executar um loop infinito se omitir volatile, pois o primeiro thread pode armazenar em cache o valor de stop. Aqui, o volatileserve como uma dica para o compilador ter um pouco mais de cuidado com as otimizações.
-1: você está dando exemplos, mas não está realmente explicando a diferença entre um volátil e um Atomicxxxx.
Jason S
70
A questão não é sobre volatile. A pergunta é sobre volatile booleanvs AtomicBoolean.
anta
26
-1: a pergunta feita especificamente sobre booleano, que é um caso único em comparação com outros tipos de dados e deve ser explicada diretamente.
John Haager
8
@ sgp15 Tem a ver com a sincronização a partir de Java 5.
Man of One Way
6
Se o valor booleano for lido por muitos threads, mas gravado por apenas um thread, volatile booleanserá suficiente. Se também houver muitos escritores, você pode precisar AtomicBoolean.
StvnBrkdll
263
Uso campos voláteis quando o referido campo é SOMENTE ATUALIZADO por seu encadeamento proprietário e o valor é lido apenas por outros encadeamentos; você pode pensar nele como um cenário de publicação / assinatura em que existem muitos observadores, mas apenas um editor. No entanto, se esses observadores precisarem executar alguma lógica com base no valor do campo e empurrar um novo valor para trás, seguirei com vars Atomic * ou bloqueios ou blocos sincronizados, o que melhor me convier. Em muitos cenários simultâneos, tudo se resume a obter o valor, compará-lo com outro e atualizar, se necessário, daí os métodos compareAndSet e getAndSet presentes nas classes Atomic *.
Verifique os JavaDocs do pacote java.util.concurrent.atomic para obter uma lista de classes Atomic e uma excelente explicação de como elas funcionam (apenas aprendi que elas são livres de bloqueios, para que tenham uma vantagem sobre bloqueios ou blocos sincronizados)
Isso é verdade, mas isso não seria um requisito bastante raro para um booleano?
28414 Robin
1
O @ Robin pensa em usá-lo para controlar uma invocação lenta de um método de inicialização.
Ustaman Sangat
Na verdade, eu acho que esse é um dos principais casos de uso.
fool4jesus 16/06
42
AtomicBooleanpossui métodos que executam suas operações compostas atomicamente e sem a necessidade de usar um synchronizedbloco. Por outro lado, volatile booleansó pode executar operações compostas se isso for feito dentro de um synchronizedbloco.
Os efeitos de memória da leitura / gravação volatile booleansão idênticos aos métodos gete respectivamente.setAtomicBoolean
Por exemplo, o compareAndSetmétodo executará atomicamente o seguinte (sem um synchronizedbloco):
if(value == expectedValue){
value = newValue;returntrue;}else{returnfalse;}
Portanto, o compareAndSetmétodo permitirá que você escreva um código que é garantido para executar apenas uma vez, mesmo quando chamado de vários threads. Por exemplo:
É garantido que apenas notifique o ouvinte uma vez (assumindo que nenhum outro encadeamento AtomicBooleanretorne falsenovamente depois de ser definido como true).
volatileA palavra-chave garante o relacionamento de antes do acontecimento entre os segmentos que compartilham essa variável. Não garante que 2 ou mais threads não se interrompam ao acessar essa variável booleana.
O acesso booleano (como no tipo primitivo) é atômico em Java. Leituras e atribuições. Portanto, nenhum outro thread "interromperá" as operações booleanas.
Maciej Biłas
1
Sinto muito, mas como isso responde à pergunta? Uma Atomic*classe envolve um volatilecampo.
cinzento
Os caches da CPU não são o principal fator para definir voláteis? Para garantir que o valor lido seja, na verdade, o que foi definido mais recentemente
jocull 11/03/19
8
AtomicBoolean vs Volatile boolean
As classes Atomic * envolvem uma primitiva volátil do mesmo tipo. Da fonte:
publicclassAtomicLongextendsNumberimplements java.io.Serializable{...privatevolatilelong value;...publicfinallong get(){return value;}...publicfinalvoid set(long newValue){
value = newValue;}
Portanto, se tudo o que você está fazendo é obter e configurar um Atomic *, é melhor ter apenas um campo volátil.
O que AtomicBoolean faz que um booleano volátil não pode alcançar?
Atômicas * aulas de dar-lhe métodos que fornecem funcionalidades mais avançadas, tais como incrementAndGet(), compareAndSet()e outros que implementam várias operações (get / incremento / set, teste / set), sem bloqueio. É por isso que as classes Atomic * são tão poderosas.
Por exemplo, se vários segmentos estiverem usando o seguinte código ++, haverá condições de corrida porque, ++na verdade, são: get, increment e set.
privatevolatile value;...// race conditions here
value++;
No entanto, o código a seguir funcionará em um ambiente multithread com segurança, sem bloqueios:
privatefinalAtomicLong value =newAtomicLong();...
value.incrementAndGet();
Também é importante observar que agrupar seu campo volátil usando a classe Atomic * é uma boa maneira de encapsular o recurso compartilhado crítico do ponto de vista de um objeto. 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 introduza condições de corrida.
O tipo primitivo booleano é atômico para operações de gravação e leitura, garante a volatilidade do princípio acontece antes. Portanto, se você precisar de um get () e um set () simples, não precisará do AtomicBoolean.
Por outro lado, se você precisar implementar alguma verificação antes de definir o valor de uma variável, por exemplo, "se verdadeiro, e depois definido como falso", será necessário executar essa operação também de maneira atômica, nesse caso, use compareAndSet e outros métodos fornecidos por AtomicBoolean, pois se você tentar implementar essa lógica com booleano volátil, precisará de alguma sincronização para garantir que o valor não seja alterado entre get e set.
Curto, crocante e direto ao ponto. volatilefunciona apenas nos casos em que o encadeamento do proprietário tem a capacidade de atualizar o valor do campo e os outros encadeamentos podem apenas ler.
Chaklader Asfak Arefe
3
Se você tiver apenas um thread modificando seu booleano, poderá usar um booleano volátil (normalmente você faz isso para definir uma stopvariável marcada no loop principal do thread).
No entanto, se você tiver vários threads modificando o booleano, use um AtomicBoolean. Senão, o seguinte código não é seguro:
boolean r =!myVolatileBoolean;
Esta operação é realizada em duas etapas:
O valor booleano é lido.
O valor booleano é gravado.
Se um outro encadeamento modificar o valor entre #1e 2#, você poderá obter um resultado errado. AtomicBooleanmétodos evitam esse problema executando etapas #1e #2atomicamente.
Respostas:
Eles são totalmente diferentes. Considere este exemplo de um
volatile
número inteiro:Se dois threads chamam a função simultaneamente,
i
pode ser 5 depois, pois o código compilado será um pouco semelhante a este (exceto que você não pode sincronizarint
):Se uma variável é volátil, todo acesso atômico a ela é sincronizado, mas nem sempre é óbvio o que realmente se qualifica como acesso atômico. Com um
Atomic*
objeto, é garantido que todo método é "atômico".Assim, se você usar um
AtomicInteger
egetAndAdd(int delta)
, pode ter certeza de que o resultado será10
. Da mesma forma, se dois threads negam umaboolean
variável simultaneamente, com umAtomicBoolean
você pode ter certeza de que possui o valor original posteriormente, com umvolatile boolean
, você não pode.Portanto, sempre que houver mais de um encadeamento modificando um campo, você precisará torná-lo atômico ou usar a sincronização explícita.
O objetivo de
volatile
é diferente. Considere este exemploSe você tiver um thread em execução
loop()
e outro chamado de threadstop()
, poderá executar um loop infinito se omitirvolatile
, pois o primeiro thread pode armazenar em cache o valor de stop. Aqui, ovolatile
serve como uma dica para o compilador ter um pouco mais de cuidado com as otimizações.fonte
volatile
. A pergunta é sobrevolatile boolean
vsAtomicBoolean
.volatile boolean
será suficiente. Se também houver muitos escritores, você pode precisarAtomicBoolean
.Uso campos voláteis quando o referido campo é SOMENTE ATUALIZADO por seu encadeamento proprietário e o valor é lido apenas por outros encadeamentos; você pode pensar nele como um cenário de publicação / assinatura em que existem muitos observadores, mas apenas um editor. No entanto, se esses observadores precisarem executar alguma lógica com base no valor do campo e empurrar um novo valor para trás, seguirei com vars Atomic * ou bloqueios ou blocos sincronizados, o que melhor me convier. Em muitos cenários simultâneos, tudo se resume a obter o valor, compará-lo com outro e atualizar, se necessário, daí os métodos compareAndSet e getAndSet presentes nas classes Atomic *.
Verifique os JavaDocs do pacote java.util.concurrent.atomic para obter uma lista de classes Atomic e uma excelente explicação de como elas funcionam (apenas aprendi que elas são livres de bloqueios, para que tenham uma vantagem sobre bloqueios ou blocos sincronizados)
fonte
boolean
var, devemos escolhervolatile boolean
.Você não pode fazer isso
compareAndSet
,getAndSet
como operação atômica com booleano volátil (a menos que, é claro, você o sincronize).fonte
AtomicBoolean
possui métodos que executam suas operações compostas atomicamente e sem a necessidade de usar umsynchronized
bloco. Por outro lado,volatile boolean
só pode executar operações compostas se isso for feito dentro de umsynchronized
bloco.Os efeitos de memória da leitura / gravação
volatile boolean
são idênticos aos métodosget
e respectivamente.set
AtomicBoolean
Por exemplo, o
compareAndSet
método executará atomicamente o seguinte (sem umsynchronized
bloco):Portanto, o
compareAndSet
método permitirá que você escreva um código que é garantido para executar apenas uma vez, mesmo quando chamado de vários threads. Por exemplo:É garantido que apenas notifique o ouvinte uma vez (assumindo que nenhum outro encadeamento
AtomicBoolean
retornefalse
novamente depois de ser definido comotrue
).fonte
volatile
A palavra-chave garante o relacionamento de antes do acontecimento entre os segmentos que compartilham essa variável. Não garante que 2 ou mais threads não se interrompam ao acessar essa variável booleana.fonte
Atomic*
classe envolve umvolatile
campo.As classes Atomic * envolvem uma primitiva volátil do mesmo tipo. Da fonte:
Portanto, se tudo o que você está fazendo é obter e configurar um Atomic *, é melhor ter apenas um campo volátil.
Atômicas * aulas de dar-lhe métodos que fornecem funcionalidades mais avançadas, tais como
incrementAndGet()
,compareAndSet()
e outros que implementam várias operações (get / incremento / set, teste / set), sem bloqueio. É por isso que as classes Atomic * são tão poderosas.Por exemplo, se vários segmentos estiverem usando o seguinte código
++
, haverá condições de corrida porque,++
na verdade, são: get, increment e set.No entanto, o código a seguir funcionará em um ambiente multithread com segurança, sem bloqueios:
Também é importante observar que agrupar seu campo volátil usando a classe Atomic * é uma boa maneira de encapsular o recurso compartilhado crítico do ponto de vista de um objeto. 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 introduza condições de corrida.
fonte
Se houver vários threads acessando a variável no nível da classe, cada thread poderá manter a cópia dessa variável em seu cache local do thread.
Tornar a variável volátil impedirá que os threads mantenham a cópia da variável no cache local do thread.
As variáveis atômicas são diferentes e permitem a modificação atômica de seus valores.
fonte
O tipo primitivo booleano é atômico para operações de gravação e leitura, garante a volatilidade do princípio acontece antes. Portanto, se você precisar de um get () e um set () simples, não precisará do AtomicBoolean.
Por outro lado, se você precisar implementar alguma verificação antes de definir o valor de uma variável, por exemplo, "se verdadeiro, e depois definido como falso", será necessário executar essa operação também de maneira atômica, nesse caso, use compareAndSet e outros métodos fornecidos por AtomicBoolean, pois se você tentar implementar essa lógica com booleano volátil, precisará de alguma sincronização para garantir que o valor não seja alterado entre get e set.
fonte
Lembre-se do IDIOM -
LER - MODIFICAR - ESCREVER isso que você não pode conseguir com materiais voláteis
fonte
volatile
funciona apenas nos casos em que o encadeamento do proprietário tem a capacidade de atualizar o valor do campo e os outros encadeamentos podem apenas ler.Se você tiver apenas um thread modificando seu booleano, poderá usar um booleano volátil (normalmente você faz isso para definir uma
stop
variável marcada no loop principal do thread).No entanto, se você tiver vários threads modificando o booleano, use um
AtomicBoolean
. Senão, o seguinte código não é seguro:Esta operação é realizada em duas etapas:
Se um outro encadeamento modificar o valor entre
#1
e2#
, você poderá obter um resultado errado.AtomicBoolean
métodos evitam esse problema executando etapas#1
e#2
atomicamente.fonte
Ambos têm o mesmo conceito, mas no booleano atômico, ele fornecerá atomicidade para a operação, caso o switch cpu ocorra no meio.
fonte