Contexto

Para que se entenda a utilidade e o funcionamento do modificador volatile, é necessário conhecer um pouquinho sobre como programas em Java armazenam e acessam variáveis nas diversas memórias disponíveis em um computador.

Tenha em mente as seguintes assertivas (bastante simplificadas):

  • O programa acessa e manipula alguns tipos de memória diferentes: registradores, cache, ram;
  • As diferentes memórias possuem performances diferentes;
  • Registrador é um tipo de memória muito cara, que fica dentro do próprio processador e por isso tem velocidade excelente;
  • Memória cache é um tipo de memória que fica próxima ao processador e normalmente pertence a um dos seguintes 3 grupos: L1 (pequena e rápida, uma por processador), L2 (maior e menos rápida, uma por processador), l3 (bem maior e menos rápida, compartilhada entre processadores);
  • Alguns computadores não possuem L2 e ou L3, por questões econômicas;
  • Memória RAM é a memória principal do computador, e a maior entre as que foram citadas. Com a tecnologia atual, ela é sempre muito mais lenta que o processador, e por isso as outras formas de memória são necessárias.

Além disso é importante saber que o compilador pode fazer algumas alterações na ordem das instruções do programa, para otimizar o processamento e o acesso à memória, veja o código:

public class Test{
   int a,b;
   public void escrever(){
      a = 1;
      b = 1;
   }
   public void ler(){
      int lidoA = a;
      int lidoB = b;
      System.out.print(a);
      System.out.println(b);
   }
}

O compilador e a JVM não provêem qualquer garantia de que as instruções serão executadas na ordem em que foram escritas pelo programador, caso eles entendam que as instruções são independentes. Então, no exemplo acima, caso uma thread chame o método escrever, e outra chame o método ler, o resultado pode ser 00,01,11 ou 10, sem que o programador possa prevê-lo inequívocamente.

Então: Para melhorar a performance, o compilador, a JVM e os processadores armazenam “cópias locais” das variáveis em locais de acesso mais fácil (cache), fazendo com que threads diferentes possam ter acesso à versões diferentes das mesmas variáveis. Além disso eles reordenam as instruções de forma a otimizar o acesso e a escrita nas diferentes memórias. Quando há um tempinho disponível as diferentes memórias são sincronizadas, mas não há como prever e garantir quando isso será feito.

Em função de tudo isso, uma determinada thread pode trabalhar sobre um conjunto de dados obsoleto (stale data),

Era necessário haver um mecanismo que garantisse que todas as threads vissem determinadas variáveis com o mesmo valor, o tempo todo.

Volatile

O modificador volatile é usado para garantir a consistência do estado de variáveis entre diferentes threads.  Cada leitura a uma variável volatile irá retornar o último valor atribuído a ela por qualquer thread. Para garantir isso as seguintes regras são válidas:

  • O processador e a JVM não podem armazenar variáveis volatile nos registradores;
  • Escritas em variáveis volatile são imediatamente enviadas dos caches para a memória principal do computador;
  • Imediatamente antes de retornar o valor de uma variável volatile, o cache correspondente é invalidado, fazendo com que o valor retornado seja aquele presente na memória principal do computador.

Além disso, instruções que envolvem variáveis volatile não podem ser reordenadas entre si, e existem grandes restrições quanto à forma como elas podem ser reordenadas com instruções relacionadas às variáveis não-volatile. De fato, instruções que operam sobre variáveis volatile funcionam como entradas e saídas de blocos synchronized, visto que elas provocam a sincronização entre o cache utilizado pela thread e a memória principal do computador.

Naturalmente variáveis volatile, implicam em perda de performance, visto que diminuem a capacidade do compilador otimizar o código, e obrigam o acesso mais frequente e não otimizado a memória principal. Então use com moderação.

Observações Finais

Esse artigo é praticamente todo válido para JVM’s anteriores à 5 e completamente válido para a JVM 5 ou as posteriores.

A JVM 5 implementou a JSR-133 que especifica o “Java Memory Model“, que nasceu da percepção de que haviam vários “buracos” na forma como programas multi-thread escritos em Java lidavam com a memória, resultando em inconsistências e baixa performance.

Leituras Importantes

  • http://informatica.hsw.uol.com.br/memoria-do-computador.htm
  • http://en.wikipedia.org/wiki/Java_Memory_Model
  • http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
 

2 Responses to Modificador volatile

  1. Carlos Alberto disse:

    Boa, gostei do texto.

  2. PH disse:

    Muito interessante.

    O ideal é aprendermos Java assim, entendendo o real motivo para o funcionamento das coisas.

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

* Copy this password:

* Type or paste password here:

4 Spam Comments Blocked so far by Spam Free Wordpress

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Set your Twitter account name in your settings to use the TwitterBar Section.