Quando exatamente você usa a palavra-chave volátil em Java?

Eu li ” Quando usar ‘volátil‘ em Java? “, Mas ainda estou confuso. Como sei quando devo marcar uma variável volátil? E se eu errar, omitindo algo volátil em algo que precise ou colocando algo volátil em algo que não precisa? Quais são as regras práticas ao descobrir quais variables ​​devem ser voláteis no código multithread?

Você basicamente usa isto quando quer deixar uma variável de membro ser acessada por múltiplos encadeamentos mas não precisa de atomicidade composta (não tenho certeza se esta é a terminologia correta).

class BadExample { private volatile int counter; public void hit(){ /* This operation is in fact two operations: * 1) int tmp = this.counter; * 2) this.counter = tmp + 1; * and is thus broken (counter becomes fewer * than the accurate amount). */ counter++; } } 

o acima é um mau exemplo, porque você precisa de atomicidade composta.

  class BadExampleFixed { private int counter; public synchronized void hit(){ /* * Only one thread performs action (1), (2) at a time * "atomically", in the sense that other threads can not * observe the intermediate state between (1) and (2). * Therefore, the counter will be accurate. */ counter++; } } 

Agora, para um exemplo válido:

  class GoodExample { private static volatile int temperature; //Called by some other thread than main public static void todaysTemperature(int temp){ // This operation is a single operation, so you // do not need compound atomicity temperature = temp; } public static void main(String[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); } } } 

Agora, por que você não pode simplesmente usar private static int temperature ? Na verdade, você pode (no sentido de que o seu programa não vai explodir ou algo assim), mas a mudança de temperature pelo outro segmento pode ou não ser “visível” para o thread principal.

Basicamente, isso significa que é possível até mesmo que seu aplicativo. continua escrevendo Today's temperature is 0 para sempre se você não usar volatile (na prática, o valor tende a se tornar visível. No entanto, você não deve arriscar não usar volátil quando necessário, pois pode levar a erros desagradáveis objects completamente construídos etc.).

Se você colocar uma palavra-chave volatile em algo que não seja volatile , isso não afetará a correção do código (ou seja, o comportamento não será alterado). Em termos de desempenho, dependerá da implementação da JVM. Em teoria, você pode ter uma pequena degradação de desempenho porque o compilador não pode fazer reordenamentos nas otimizações, ter que invalidar o cache da CPU, etc., mas o compilador pode provar que seu campo não pode ser acessado por vários threads e remover o efeito de volatile Palavra-chave completamente e compilá-lo para instruções idênticas.

EDITAR:
Resposta a este comentário:

Ok, mas por que não podemos tornar a temperatura sincronizada hoje e criar um getter sincronizado para a temperatura?

Você pode e vai se comportar corretamente. Qualquer coisa que você pode com o volatile pode ser feito com synchronized , mas não vice-versa. Existem dois motivos pelos quais você pode preferir a volatile se puder:

  1. Menos propenso a bugs: Depende do contexto, mas em muitos casos o uso de volatile é menos propenso a bugs de concorrência, como bloquear enquanto segura a trava, deadlocks etc.
  2. Mais desempenho: na maioria das implementações de JVM, o volatile pode ter um rendimento significativamente maior e melhor latência. No entanto, na maioria das aplicações, a diferença é pequena demais para importar.

O volátil é mais útil em algoritmos livres de bloqueio. Você marca a variável mantendo os dados compartilhados como voláteis quando você não está usando o bloqueio para acessar essa variável e deseja que as alterações feitas por um thread sejam visíveis em outro, ou deseja criar uma relação “acontece após” para garantir que o cálculo seja não reordenado, novamente, para garantir que as alterações se tornem visíveis no momento apropriado.

O JMM Cookbook descreve quais operações podem ser reordenadas e quais não podem.

O volatile também pode ser usado para publicar com segurança objects imutáveis ​​em um ambiente multithread.

Declarar um campo como public volatile ImmutableObject foo assegura que todos os threads sempre verão a referência de instância atualmente disponível.

Veja Java Concorrency in Practice para mais sobre esse tópico.

volatile palavra-chave volatile garante que o valor da variável volátil sempre será lido na memory principal e não no cache local do Thread.

Do tutorial de simultaneidade java:

O uso de variables ​​voláteis reduz o risco de erros de consistência de memory, porque qualquer gravação em uma variável volátil estabelece um relacionamento acontece antes com leituras subseqüentes dessa mesma variável.

Isso significa que as alterações em uma variável volátil são sempre visíveis para outros segmentos. Isso também significa que, quando um thread lê uma variável volátil, ele vê não apenas a última alteração no volátil, mas também os efeitos colaterais do código que levaram à alteração.

Em relação à sua consulta:

Como sei quando devo marcar uma variável volátil? Quais são as regras práticas ao descobrir quais variables ​​devem ser voláteis no código multithread?

Se você acha que todos os threads do leitor sempre obtêm o valor mais recente de uma variável, é necessário marcar a variável como volatile

Se você tiver um encadeamento de gravador para modificar o valor da variável e vários encadeamentos do leitor para ler o valor da variável , o modificador volátil garante a consistência da memory.

Se você tiver vários encadeamentos para gravar e ler variables, o modificador volatile sozinho não garante a consistência da memory. Você tem que synchronize o código ou usar construções de concorrência de alto nível, como Locks , Concurrent Collections , Atomic variables etc.

SE relacionado perguntas / artigos:

Explicação da variável volátil em documentos Java

Diferença entre volátil e sincronizado em Java

artigo javarevisited

Na verdade discordo do exemplo dado na resposta mais votada, que eu saiba, NÃO ilustra adequadamente a semântica volátil de acordo com o modelo de memory Java. O volátil tem uma semântica muito mais complexa.

No exemplo fornecido, o thread principal pode continuar imprimindo “A temperatura de hoje é 0” para sempre, mesmo se houver outro thread em execução que supostamente atualize a temperatura se esse outro thread nunca for programado.

Uma maneira melhor de ilustrar a semântica volátil é com duas variables.

Para simplificar, vamos supor que a única maneira de atualizar as duas variables ​​é através do método “setTemperatures” .

Para simplificar, vamos supor que apenas 2 threads estão sendo executados, thread principal e thread 2.

 //volatile variable private static volatile int temperature; //any other variable, could be volatile or not volatile doesnt matter. private static int yesterdaysTemperature //Called by other thread(s) public static void setTemperatures(int temp, int yestemp){ //thread updates yesterday's temperature yesterdaysTemperature = yestemp; //thread updates today's temperature. //This instruction can NOT be moved above the previous instruction for optimization. temperature = temp; } 

as duas últimas instruções de atribuição NÃO podem ser reordenadas para fins de otimização pelo compilador, pelo tempo de execução ou pelo hardware.

 public static void main(String[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); System.out.println("Yesterday's temperature was "+yesterdaysTemperature ); } } 

Uma vez que o thread principal lê a temperatura variável volátil (no processo de impressão),

1) Há uma garantia de que ele verá o valor escrito mais recente dessa variável volátil, independentemente de quantos segmentos estão gravando nela, independentemente de qual método eles estão atualizando, sincronizados ou não.

2) Se a instrução system.out no thread principal for executada, após o instante de tempo no qual o thread 2 executou a instrução temperature = temp, a temperatura de ontem e a temperatura de hoje terão a garantia de imprimir os valores definidos neles pelo thread 2 quando correu a instrução temperature = temp.

Essa situação fica muito mais complexa se a) Vários encadeamentos estiverem em execução e b) Existem outros methods além do método setTemperatures que podem atualizar a variável temperatura de ontem e a temperatura atual que estão sendo ativamente chamadas por esses outros encadeamentos. Acho que seria necessário um artigo de tamanho decente para analisar as implicações com base em como o Modelo de Memória Java descreve a semântica volátil.

Em suma, a tentativa de usar apenas a volatilidade para synchronization é extremamente arriscada, e seria melhor manter a synchronization dos seus methods.

http://mindprod.com/jgloss/volatile.html

“A palavra-chave volátil é usada em variables ​​que podem ser modificadas simultaneamente por outros threads.”

“Já que outras threads não podem ver variables ​​locais, nunca há necessidade de marcar variables ​​locais voláteis. Você precisa sincronizar para coordenar mudanças em variables ​​de diferentes threads, mas geralmente a volatilidade fará apenas para analisá-las.”

Voltalie Significa Keep changing value.O valor dessa variável nunca será armazenado em cache localmente: todas as leituras e gravações irão diretamente para a “memory principal” .Em outras palavras, o compilador Java e o Thread que não armazenam em cache o valor dessa variável e sempre leem da memory principal.