Volátil vs Estático em Java

Respostas:

365

Declarar uma variável estática em Java significa que haverá apenas uma cópia, não importa quantos objetos da classe sejam criados. A variável estará acessível mesmo sem a Objectscriação. No entanto, os threads podem ter valores armazenados em cache localmente.

Quando uma variável é volátil e não estática , haverá uma variável para cada Object. Portanto, aparentemente não há diferença de uma variável normal, mas totalmente diferente da estática . No entanto, mesmo com Objectcampos, um encadeamento pode armazenar em cache um valor variável localmente.

Isso significa que se dois encadeamentos atualizam uma variável do mesmo objeto simultaneamente e a variável não é declarada volátil, pode haver um caso em que um dos encadeamentos tenha em cache um valor antigo.

Mesmo se você acessar um valor estático através de vários threads, cada thread poderá ter sua cópia em cache local! Para evitar isso, você pode declarar a variável como estática volátil e isso forçará o thread a ler cada vez que o valor global.

No entanto, volátil não substitui a sincronização adequada!
Por exemplo:

private static volatile int counter = 0;

private void concurrentMethodWrong() {
  counter = counter + 5;
  //do something
  counter = counter - 5;
}

A execução concurrentMethodWrongsimultânea muitas vezes pode levar a um valor final de contador diferente de zero!
Para resolver o problema, você precisa implementar um bloqueio:

private static final Object counterLock = new Object();

private static volatile int counter = 0;

private void concurrentMethodRight() {
  synchronized (counterLock) {
    counter = counter + 5;
  }
  //do something
  synchronized (counterLock) {
    counter = counter - 5;
  }
}

Ou use a AtomicIntegerclasse.

Stivlo
fonte
7
O modificador volátil garante que qualquer encadeamento que lê um campo veja o valor gravado mais recentemente; portanto, é necessário que a variável seja compartilhada entre vários encadeamentos e você precise desse recurso, isso depende do seu caso de uso.
stivlo
5
Qual é o cache quando você diz "localmente em cache"? Cache da CPU, algum tipo de cache da JVM?
precisa
6
@mertinan sim, a variável pode estar em um cache mais próximo do processador ou núcleo. Consulte cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html para obter mais detalhes.
Stivlo
15
'volátil' não implica 'uma variável por objeto'. Ausência de 'estático' faz isso. -1 por não esclarecer esse equívoco elementar por parte do OP.
Marquês de Lorne
27
@EJP Eu pensei que a frase "Declarando uma variável como volátil, haverá uma variável para cada Objeto. Então, aparentemente, não há diferença em relação a uma variável normal", estava explicando que eu adicionei e não estática , fique à vontade para editar o artigo e melhorar a redação para tornar isso mais claro.
stivlo
288

Diferença entre estático e volátil:

Variável estática : se dois Threads (suponha t1e t2) estão acessando o mesmo objeto e atualizando uma variável declarada como estática, isso significa t1e t2podem fazer sua própria cópia local do mesmo objeto (incluindo variáveis ​​estáticas) em seu respectivo cache, portanto atualize feito pela t1variável estática em seu cache local não refletirá na variável estática do t2cache.

As variáveis ​​estáticas são usadas no contexto de Objeto, onde a atualização feita por um objeto refletirá em todos os outros objetos da mesma classe, mas não no contexto de Thread, em que a atualização de um thread na variável estática refletirá as alterações imediatamente em todos os objetos. threads (no cache local).

Variável volátil : se dois Threads (suponha t1e t2) estão acessando o mesmo objeto e atualizando uma variável que é declarada como volátil, isso significa t1e t2pode fazer seu próprio cache local do Objeto, exceto a variável que é declarada como volátil . Portanto, a variável volátil terá apenas uma cópia principal que será atualizada por diferentes threads e a atualização feita por um thread na variável volátil será refletida imediatamente no outro Thread.

Som
fonte
6
Olá @Som, por favor, corrija-me se estiver errado. Mas você não acha que a declaração " mas não no contexto do Thread em que a atualização de um thread na variável estática refletirá as alterações imediatamente em todos os threads (no cache local). " Deveria estar ", mas não no contexto do Thread em que a atualização de um thread na variável estática <<NÃO>> refletirá as alterações imediatamente em todos os threads (no cache local). "
Jaikrat
@ Jaikrat Sim, isso foi muito confuso para mim. Meu entendimento é que você está certo e que esta resposta está errada, como está escrito. Eu também gostaria de ser corrigido se estiver errado.
Stuart
Os Threads @Jaikrat não armazenam em cache as variáveis ​​estáticas, mas referem-se às variáveis ​​estáticas atualizadas.
Som
@ Som Então, você gostaria de corrigir o para e remover, mas não no contexto do Thread . Isso é muito confuso. Graças
Jaikrat
Infelizmente, esta resposta está incorreta. Em CPUs modernas, mesmo uma volatilevariável pode ser compartilhada entre caches de CPU distintos. Isso não apresenta problema porque o cache negocia a propriedade exclusiva da linha de cache antes de modificá-lo.
11309 David Schwartz
32

Além de outras respostas, gostaria de adicionar uma imagem para ela (a foto facilita a compreensão)

insira a descrição da imagem aqui

staticvariáveis ​​podem ser armazenadas em cache para threads individuais. No ambiente multithread, se um thread modificar seus dados em cache, isso pode não refletir em outros threads, pois eles têm uma cópia deles .

volatileA declaração garante que os threads não armazenem em cache os dados e use apenas a cópia compartilhada .

fonte da imagem

mrsrinivas
fonte
1
variáveis ​​estáticas são compartilhadas entre objetos em um thread? Isso deve ler que as variáveis ​​estáticas são compartilhadas entre os objetos, todos os objetos, independentemente dos encadeamentos.
Cquezel
1
msgstr "variáveis ​​voláteis são compartilhadas entre vários threads (também objetos)." Volátil não altera como as variáveis ​​são compartilhadas entre vários threads ou objetos. Altera como o tempo de execução tem permissão para armazenar em cache o valor.
Cquezel
1
Seu comentário sobre variáveis ​​estáticas também se aplica a não estáticas e o "será armazenado em cache" e "não será refletido" provavelmente deve ser reformulado "pode ​​ser armazenado em cache" e "pode ​​não ser refletido".
Cquezel
4
Eu estava muito confuso. essa foto apagou todas as minhas perguntas!
vins
5

Eu penso statice volatilenão tenho nenhuma relação. Eu sugiro que você leia o tutorial em java para entender o Acesso Atômico , e por que usar o acesso atômico, entender o que está intercalado , você encontrará a resposta.

Amitābha
fonte
4

Em termos simples,

  1. static : as staticvariáveis ​​estão associadas à classe , e não a qualquer objeto . Cada instância da classe compartilha uma variável de classe, que está em um local fixo na memória

  2. volátil : esta palavra-chave é aplicável a variáveis ​​de classe e instância .

O uso de variáveis ​​voláteis reduz o risco de erros de consistência da memória, porque qualquer gravação em uma variável volátil estabelece um relacionamento de antes do acontecimento com leituras subsequentes dessa mesma variável. Isso significa que alterações em uma variável volátil são sempre visíveis para outros threads

Dê uma olhada neste artigo por Javin Paul entender variáveis voláteis em uma maneira melhor.

insira a descrição da imagem aqui

Na ausência de volatilepalavra-chave, o valor da variável na pilha de cada thread pode ser diferente. Fazendo a variável como volatile, todos os threads obterão o mesmo valor em sua memória de trabalho e erros de consistência de memória foram evitados.

Aqui, o termo variablepode ser staticvariável (classe) ou variável instance(objeto).

Em relação à sua consulta:

De qualquer forma, um valor de variável estática também será um valor para todos os encadeamentos; por que deveríamos optar pelo volátil?

Se eu precisar de instancevariável no meu aplicativo, não posso usar a staticvariável. Mesmo no caso de staticvariável, a consistência não é garantida devido ao cache do Thread, conforme mostrado no diagrama.

O uso de volatilevariáveis ​​reduz o risco de erros de consistência da memória, porque qualquer gravação em uma variável volátil estabelece um relacionamento de antes do acontecimento com leituras subsequentes dessa mesma variável. Isso significa que as alterações em uma variável volátil são sempre visíveis para outros threads.

Além disso, também significa que, quando um encadeamento lê uma variável volátil, ele vê não apenas as alterações mais recentes no volátil, mas também os efeitos colaterais do código que levou à mudança => erros de consistência na memória ainda são possíveis com variáveis ​​voláteis . Para evitar efeitos colaterais, você deve usar variáveis ​​sincronizadas. Mas há uma solução melhor em java.

Usar o acesso simples a variáveis ​​atômicas é mais eficiente do que acessar essas variáveis ​​por meio de código sincronizado

Algumas das classes no java.util.concurrentpacote fornecem métodos atômicos que não dependem da sincronização.

Consulte este artigo de controle de simultaneidade de alto nível para obter mais detalhes.

Dê uma olhada especialmente nas variáveis ​​atômicas .

Perguntas relacionadas ao SE:

Volatile Vs Atomic

AtomicBoolean vs Volatile boolean

Diferença entre volátil e sincronizado em Java

Ravindra babu
fonte
Eu realmente aprecio esta resposta. Eu sabia o que é o volatileanterior, mas, esta resposta me esclarece muito por que ainda preciso usar volatilecom a staticvariável
Chaklader Asfak Arefe
volátil: esta palavra-chave é aplicável a variáveis ​​de classe e instância. A afirmação que você disse acima está incorreta em relação à classe. apenas duas palavras-chave aplicáveis ​​à variável são voláteis e transitórias. tão volátil não se aplica à classe.
ASR
volátil é aplicável a variáveis ​​de classe (estáticas). Confira os links singleton bloqueados duplos no google e você pode descobrir que seu entendimento está errado. stackoverflow.com/questions/18093735/…
Ravindra babu
volatil estático privado é declaração válida.
Ravindra babu
0

o acesso a valores variáveis ​​voláteis será direto da memória principal. Ele deve ser usado apenas em ambientes com vários threads. A variável estática será carregada uma vez. Se for usado no ambiente de thread único, mesmo que a cópia da variável seja atualizada e não haverá danos ao acessá-lo, pois há apenas um thread.

Agora, se a variável estática for usada no ambiente com vários threads, haverá problemas se alguém esperar o resultado desejado. Como cada encadeamento possui sua própria cópia, qualquer incremento ou decréscimo na variável estática de um encadeamento pode não refletir em outro encadeamento.

se se espera resultados desejados da variável estática, use volátil com estática no multi-threading, tudo será resolvido.

Aslam anwer
fonte
0

Não tenho certeza se as variáveis ​​estáticas estão armazenadas em cache na memória local do encadeamento ou NÃO. Mas quando eu executei dois threads (T1, T2) acessando o mesmo objeto (obj) e quando a atualização feita pelo thread T1 na variável estática, isso se refletiu no T2.

user2779355
fonte
-2

Se declararmos uma variável como estática, haverá apenas uma cópia da variável. Portanto, sempre que diferentes threads acessarem essa variável, haverá apenas um valor final para a variável (pois há apenas um local de memória alocado para a variável).

Se uma variável for declarada como volátil, todos os threads terão sua própria cópia da variável, mas o valor será retirado da memória principal. Portanto, o valor da variável em todos os threads será o mesmo.

Portanto, nos dois casos, o ponto principal é que o valor da variável é o mesmo em todos os segmentos.

Jitendra Nalwaya
fonte
15
Se uma variável for declarada como volátil, todos os encadeamentos terão sua própria cópia da variável, mas o valor será retirado da memória principal. => certo. Portanto, o valor da variável em todos os threads será o mesmo. => errado, cada thread usará o mesmo valor para o mesmo objeto, mas cada objeto terá sua própria cópia.
Stivlo 30/10