Quando preciso usar o AtomicBoolean em Java?

Respostas:

244

Quando vários threads precisam verificar e alterar o booleano. Por exemplo:

if (!initialized) {
   initialize();
   initialized = true;
}

Isso não é seguro para threads. Você pode corrigi-lo usando AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}
Bozho
fonte
51
Não parece um exemplo do mundo real - outro segmento pode ver truequando initialize()não foi concluído. Portanto, ele funciona apenas se outros threads não se importam com a conclusão de initialize().
axtavt
6
@axtavt: Eu acho que é um exemplo do mundo real perfeitamente válido se initializedestiver sendo simplesmente usado para garantir que um e apenas um thread invoque o initialize()método. Obviamente, initializedser verdade não significa que a inicialização tenha sido concluída definitivamente neste caso; portanto, talvez um termo um pouco diferente seja melhor aqui. Mais uma vez, depende do que está sendo usado.
precisa saber é o seguinte
14
você precisaria de 2 booleanos para initStarted e initCompleted, então o primeiro encadeamento define initStarted e chama initialise (), o restante espera até que initCompleted seja verdadeiro.
Martin
3
@ Ozoz - lê e grava em campos booleanos são atômicos, certo ?, Agora, volátil me fornece o valor mais recente do campo booleano. Então, efetivamente, não volatile booleanseria o mesmo que AtomicBoolean?
TheLostMind
2
@ Martin: Não há maneira direta de esperar que um booleano se torne verdadeiro; você precisa de mecanismos adicionais. A abordagem mais sensata é usar um synchronizedbloco; nesse caso, você não precisa mais de um AtomicBoolean, apenas um volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }Vai garantir que apenas um thread chama initialize, e que todos os outros tópicos esperar por ele ter feito isso, desde que initializedé marcado volatile.)
ruach
52

Aqui estão as anotações (do livro de Brian Goetz ) que eu fiz, que podem ser úteis para você

Classes AtomicXXX

  • fornecer implementação de comparação e troca sem bloqueio

  • Aproveita o suporte fornecido pelo hardware (a instrução CMPXCHG na Intel) Quando muitos threads estão executando seu código que usa essa API de simultaneidade atômica, eles são dimensionados muito melhor do que o código que usa monitores / sincronização no nível de objeto. Como os mecanismos de sincronização do Java fazem a espera do código, quando há muitos threads em execução nas seções críticas, uma quantidade substancial de tempo da CPU é gasta no gerenciamento do próprio mecanismo de sincronização (espera, notificação, etc.). Como a nova API usa construções no nível de hardware (variáveis ​​atômicas) e espera e bloqueia algoritmos para implementar a segurança de encadeamento, muito mais tempo da CPU é gasto "realizando coisas" em vez de gerenciar a sincronização.

  • além de oferecer melhor rendimento, eles também oferecem maior resistência a problemas de vivacidade, como impasse e inversão de prioridade.

Aravind Yarram
fonte
34

Há duas razões principais pelas quais você pode usar um booleano atômico. Primeiro é mutável, você pode passá-lo como referência e alterar o valor associado ao próprio booleano, por exemplo.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

e na classe someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Mais importante, porém, seu encadeamento é seguro e pode indicar aos desenvolvedores que mantêm a classe, que essa variável deve ser modificada e lida em vários encadeamentos. Se você não usar um AtomicBoolean, deverá sincronizar a variável booleana que estiver usando, declarando-a volátil ou sincronizada em torno da leitura e gravação do campo.

John Vint
fonte
4
Pelo amor de Deus, isso era apenas para mostrar a mutabilidade do próprio objeto. Eu escrevi isso especificamente para fins de demonstração.
quer
E ainda mais, se isso era tudo o que estava acontecendo, então sim, ele sempre retornará verdadeiro
John Vint
Isso não está provando se é ou não é seguro para threads. Eu posso terminar meus trechos de código para tornar a classe muito segura para threads, mas isso apenas mata meu ponto.
precisa
1
Eu acho que apenas o Volatile não é suficiente. Pense em uma situação em que dois threads que lêem e gravam o mesmo valor diretamente da memória principal, não há sincronização entre esses threads - pois podem ocorrer problemas de simultaneidade.
Shay Tsadok
1
Você está certo de que não seria suficiente para operações atômicas de conjunto e verificação, embora não houvesse contexto suficiente do OP para fazer essa suposição. Dizer que volátil pode não ser suficiente é sempre verdade, dependendo da situação, é claro.
precisa
18

A AtomicBooleanclasse fornece um valor booleano que você pode atualizar atomicamente. Use-o quando você tiver vários threads acessando uma variável booleana.

A visão geral do pacote java.util.concurrent.atomic fornece uma boa descrição de alto nível do que as classes neste pacote fazem e quando usá-las. Eu também recomendaria o livro Java Concurrency in Practice, de Brian Goetz.

Cameron Skinner
fonte
5

Trecho da descrição do pacote

Descrição do pacote java.util.concurrent.atomic: Um pequeno conjunto de ferramentas de classes que suportam a programação segura de threads sem bloqueio em variáveis ​​únicas.

As especificações desses métodos permitem que as implementações empregem instruções atômicas eficientes no nível da máquina, disponíveis em processadores contemporâneos.

Instâncias das classes AtomicBoolean, AtomicInteger, AtomicLong e AtomicReference fornecem acesso e atualizações para uma única variável do tipo correspondente. [...]

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 de ler uma variável volátil.
  • O conjunto possui os efeitos de memória da gravação (atribuição) de uma variável volátil.
  • fracoCompareAndSet lê atomicamente e escreve condicionalmente uma variável, é ordenado com relação a outras operações de memória nessa variável, mas age como uma operação de memória não volátil comum.
  • compareAndSet e todas as outras operações de leitura e atualização, como getAndIncrement, têm os efeitos de memória de leitura e gravação de variáveis ​​voláteis.
OscarRyz
fonte