O que AtomicBoolean faz que um booleano volátil não pode alcançar?
O que AtomicBoolean faz que um booleano volátil não pode alcançar?
Respostas:
Eles são totalmente diferentes. Considere este exemplo de um volatile
número inteiro:
volatile int i = 0;
void incIBy5() {
i += 5;
}
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 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 AtomicInteger
e getAndAdd(int delta)
, pode ter certeza de que o resultado será 10
. Da mesma forma, se dois threads negam uma boolean
variável simultaneamente, com um AtomicBoolean
você 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
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
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 volatile
serve como uma dica para o compilador ter um pouco mais de cuidado com as otimizações.
volatile
. A pergunta é sobre volatile boolean
vs AtomicBoolean
.
volatile boolean
será suficiente. Se também houver muitos escritores, você pode precisar AtomicBoolean
.
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)
boolean
var, devemos escolher volatile 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).
AtomicBoolean
possui métodos que executam suas operações compostas atomicamente e sem a necessidade de usar um synchronized
bloco. Por outro lado, volatile boolean
só pode executar operações compostas se isso for feito dentro de um synchronized
bloco.
Os efeitos de memória da leitura / gravação volatile boolean
são idênticos aos métodos get
e respectivamente.set
AtomicBoolean
Por exemplo, o compareAndSet
método executará atomicamente o seguinte (sem um synchronized
bloco):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
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:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
É garantido que apenas notifique o ouvinte uma vez (assumindo que nenhum outro encadeamento AtomicBoolean
retorne false
novamente depois de ser definido como true
).
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.
Atomic*
classe envolve um volatile
campo.
AtomicBoolean vs Volatile boolean
As classes Atomic * envolvem uma primitiva 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;
}
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.
private volatile value;
...
// race conditions here
value++;
No entanto, o código a seguir funcionará em um ambiente multithread com segurança, sem bloqueios:
private final AtomicLong value = new AtomicLong();
...
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.
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.
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.
Lembre-se do IDIOM -
LER - MODIFICAR - ESCREVER isso que você não pode conseguir com materiais voláteis
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:
boolean r = !myVolatileBoolean;
Esta operação é realizada em duas etapas:
Se um outro encadeamento modificar o valor entre #1
e 2#
, você poderá obter um resultado errado. AtomicBoolean
métodos evitam esse problema executando etapas #1
e #2
atomicamente.
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.