Pergunta

No trabalho de hoje, me deparei com a palavra-chave volatile em Java. Não sendo muito familiarizado com ele, achei que esta explicação:

Java teoria e prática: Gerenciando a volatilidade

Dado o detalhe em que esse artigo explica a palavra-chave em questão, você nunca usá-lo ou você poderia ver um caso em que você poderia usar esta palavra-chave da maneira correta?

Foi útil?

Solução

volatile tem semântica para a visibilidade de memória. Basicamente, o valor de um campo volatile se torna visível para todos os leitores (outros segmentos, em particular), após uma conclui operação de escrita sobre ele. Sem volatile, os leitores podem ver alguns valor não atualizado.

Para responder à sua pergunta: Sim, eu uso uma variável volatile para controlar se algum código continua a loop. O loop testa o valor volatile e continua se for true. A condição pode ser configurado para false chamando um método "stop". O loop vê false e termina quando se testa o valor após a execução concluída método stop.

O livro " Java Concurrency in Practice ", que eu recomendo, dá uma boa explicação de volatile. Este livro é escrito pela mesma pessoa que escreveu o artigo da IBM que é referenciado na questão (na verdade, ele cita seu livro na parte inferior do mesmo artigo). Meu uso de volatile é o que seu artigo chama de "padrão 1 estatuto de bandeira."

Se você quiser saber mais sobre como obras volatile sob o capô, ler sobre o Java modelo de memória . Se você quer ir além desse nível, confira um livro arquitetura bom computador como Hennessy & Patterson e ler sobre coerência de cache e consistência cache.

Outras dicas

“... as garantias modificador voláteis que qualquer segmento que lê um campo verá o valor mais recentemente escrito”. - Josh Bloch
Se você estiver pensando em usar volatile, ler sobre o pacote java.util.concurrent que lida com o comportamento atômica.
O post Wikipedia em um padrão Singleton mostra voláteis em uso.

ponto importante sobre volatile:

  1. Sincronização em Java é possível usando Java palavras-chave synchronized e volatile e fechaduras.
  2. Em Java, não podemos ter variável synchronized. Usando synchronized palavra-chave com uma variável é ilegal e irá resultar em erro de compilação. Em vez de usar a variável synchronized em Java, você pode usar a variável java volatile, que irá instruir tópicos JVM para ler o valor da variável volatile da memória principal e não armazená-lo localmente.
  3. Se uma variável não é compartilhado entre vários tópicos, então não há necessidade de usar a palavra-chave volatile.

fonte

Exemplo de uso de volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

Estamos criando exemplo preguiçosamente no momento da primeira solicitação vem.

Se não fizermos a variável _instance volatile então a discussão que está a criar a instância de Singleton não é capaz de se comunicar com o outro segmento. Então, se Thread A é a criação de instância Singleton e apenas após a criação, a CPU corrompe etc, todos os outros segmentos não será capaz de ver o valor de _instance como não nulo e eles vão acreditar que ainda é atribuído nulo.

Por que isso acontece? Porque tópicos leitor não está fazendo qualquer bloqueio e até que o segmento escritor sai de um bloco sincronizado, a memória não será sincronizado eo valor de _instance não será atualizado na memória principal. Com a palavra-chave volátil em Java, este é tratado pelo próprio Java e tais alterações serão visíveis por todos os tópicos leitor.

Conclusão : palavra-chave volatile também é usado para comunicar o conteúdo da memória entre threads.

Exemplo de utilização sem voláteis:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

O código acima não é thread-safe. Embora ele verifica o valor de exemplo, mais uma vez no interior do bloco sincronizado (por motivos de desempenho), o compilador JIT pode reorganizar o código de bytes de uma forma que a referência para o exemplo é definida antes de o construtor tenha terminado a sua execução. Isto significa que o método getInstance () retorna a um objecto que não pode ter sido inicializado completamente. Para tornar o código thread-safe, a palavra-chave volátil pode ser utilizado desde Java 5 para a variável de instância. Variáveis ??que são marcados como get volátil só é visível para outros tópicos uma vez que o construtor do objeto tenha terminado sua execução completamente.
Fonte

 enter descrição da imagem aqui

uso volatile em Java :

Os iteradores fail-fast são tipicamente implementada utilizando um contador volatile no objeto lista.

  • Quando a lista for atualizada, o contador é incrementado.
  • Quando um Iterator é criado, o valor atual do contador é incorporado no objeto Iterator.
  • Quando é realizada uma operação Iterator, o método compara os dois valores de contador e lança um ConcurrentModificationException se eles são diferentes.

A implementação de iterators fail-safe é tipicamente leve. Eles geralmente dependem de propriedades de estruturas de dados da lista de implementação específica. Não há um padrão geral.

volátil é muito útil para tópicos de parada.

Não que você deve escrever seus próprios tópicos, Java 1.6 tem um monte de piscinas agradáveis ??de rosca. Mas se você tem certeza você precisa de um fio, você precisa saber como pará-lo.

O uso padrão I para threads é:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

Observe como não há necessidade de sincronização

Um exemplo comum para a utilização volatile é a utilização de uma variável volatile boolean como uma bandeira para encerrar uma rosca. Se você já começou uma discussão, e você quer ser capaz de interrompê-la com segurança a partir de um segmento diferente, você pode ter o fio de verificar periodicamente uma bandeira. Para pará-lo, definir o sinalizador para true. Ao fazer a volatile bandeira, você pode garantir que o fio que está verificando isso vai ver que foi definido na próxima vez que ele verifica-lo sem nem mesmo ter de usar um bloco synchronized.

Uma variável declarada com palavra-chave volatile, tem duas principais qualidades que o tornam especial.

  1. Se temos uma variável volátil, não pode ser armazenada em cache em (microprocessador) de memória cache do computador por qualquer segmento. Acesso sempre aconteceu a partir da memória principal.

  2. Se houver um gravação operação indo em uma variável volátil, e de repente um operação de leitura é solicitado, é garantido que o operação de gravação será concluída antes da operação de leitura .

Segundo acima qualidades deduzir que

  • Todas as threads de leitura uma variável volátil vai definitivamente ler o último valor. Porque nenhum valor em cache pode contaminá-la. E também o pedido de leitura será concedida somente após a conclusão da operação de gravação atual.

E, por outro lado,

  • Se continuar a investigar o # 2 que mencionei, podemos ver que a palavra-chave volatile é uma maneira ideal para manter uma variável compartilhada que tem o número 'n' de ler tópicos e apenas um segmento de gravação para acessá-lo. Assim que adicionar a palavra-chave volatile, ele é feito. Sem qualquer outra sobrecarga sobre segurança do thread.

Conversly,

não pode fazer uso de palavra-chave volatile unicamente, para satisfazer uma variável compartilhada que tem mais de um tópicos de escrita acessá-lo .

Sim, volátil deve ser utilizado sempre que quiser uma variável mutável para ser acessado por vários segmentos. Não é usecase muito comum, porque normalmente você precisa para realizar mais do que uma única operação atômica (por exemplo, verificar o estado de variável antes de modificá-lo), caso em que você usaria um bloco sincronizado vez.

Ninguém mencionou o tratamento de leitura e operação de gravação por muito tempo e double tipo de variável. Leituras e gravações são operações atômicas para as variáveis ??de referência e para a maioria das variáveis ??primitivas, exceto por longos e duplos tipos de variáveis, que deve usar a palavra-chave volátil para ser operações atômicas. @link

Na minha opinião, outros de parar segmento no qual palavra-chave volátil é utilizado dois cenários importantes são:

  1. verifiquei mecanismo de bloqueio . Usado frequentemente em design Singleton padronizar. Neste necessidades objeto singleton para ser declarado volátil .
  2. espúrias wakeups . Thread pode às vezes acorda de chamada de espera, mesmo se não notificar chamada foi emitido. Esse comportamento é chamado de despertar supurious. Isto pode ser contrariado utilizando uma variável condicional (sinalizador booleano). Coloque a chamada wait () em um loop while enquanto a bandeira é verdade. Então, se fio acorda de chamada de espera devido a quaisquer outros que notificar / razões notifyAll seguida, ele encontra bandeira ainda é verdade e, portanto, chamadas de esperar novamente. Antes da chamada notificar definir este sinalizador para true. Neste caso, o sinalizador booleano é declarado como volátil .

Você vai precisar usar palavra-chave 'volátil', ou 'sincronizado' e quaisquer outras ferramentas de controle de concorrência e técnicas que você pode ter à sua disposição se você está desenvolvendo um aplicativo com vários segmentos. Exemplo de tal aplicação é aplicações de computador.

Se você estiver desenvolvendo um aplicativo que seria implantado para servidor de aplicação (Tomcat, JBoss AS, Glassfish, etc) você não tem a alavanca de controle de simultaneidade-se como já abordado pelo servidor de aplicativos. Na verdade, se eu me lembrava corretamente a EE padrão Java proibir qualquer controle de concorrência em servlets e EJBs, uma vez que faz parte da 'infra-estrutura' camada que você deveria ser libertado de manuseá-lo. Você só faz o controle de concorrência em tal app se você está implementando singleton objetos. Isto mesmo já abordadas se você malha seus componentes usando frameworkd como Spring.

Assim, na maioria dos casos de desenvolvimento Java, onde a aplicação é uma aplicação web e usando o framework IoC como Spring ou EJB, você não precisa usar 'volátil'.

volatile só garante que todos os segmentos, até a si mesmos, estão incrementando. Por exemplo: um contador vê a mesma face da variável ao mesmo tempo. Ela não é usada em vez de sincronizado ou material atômico ou outra, completamente faz as leituras sincronizado. Por favor, não compará-lo com outras palavras-chave java. Como mostra o exemplo abaixo operações variáveis ??voláteis também são atômicas eles falham ou suceder ao mesmo tempo.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

Mesmo que você colocar os resultados voláteis ou não será sempre diferente. Mas se você usar AtomicInteger como abaixo os resultados serão sempre iguais. Este é o mesmo com sincronizado também.

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

Sim, eu usá-lo muito - ele pode ser muito útil para código multi-thread. O artigo que você apontou é uma boa. Embora há duas coisas importantes a ter em mente:

  1. Você só deve usar volátil se você entender completamente o que ele faz e como ele difere de sincronizado. Em muitas situações, aparece voláteis, na superfície, para ser um mais simples alternativa a performance sincronizada, quando muitas vezes uma melhor a compreensão da volátil faria claro que sincronizados é a única opção que iria funcionar.
  2. volátil não realmente trabalhar em um monte de JVMs mais velhos, embora sincronizada faz. Eu lembro de ter visto um documento que referenciados os diversos níveis de suporte em diferentes JVMs, mas infelizmente não posso encontrá-lo agora. Definitivamente olhar para ele se você estiver usando Java pré 1.5 ou se você não tem controle sobre as JVMs que o programa será executado.

Absolutamente, sim. (E não apenas em Java, mas também em C #.) Há momentos em que você precisa para obter ou definir um valor que é garantido para ser uma operação atômica em seu determinada plataforma, um int ou booleano, por exemplo, mas não necessitam de a sobrecarga de bloqueio rosca. A palavra-chave volátil permite garantir que quando você lê o valor que você obtenha o atual valor e não um valor em cache que acabou de ser tornada obsoleta por uma gravação em outro segmento.

Cada fio de acesso a um campo volátil irá ler seu valor atual antes de continuar, em vez de (potencialmente) usando um valor em cache.

Apenas variável de membro podem ser voláteis ou transitória.

Existem dois usos diferentes de palavra-chave volátil.

  1. Previne JVM da leitura de valores de registo (assumir como cache), e as forças de seu valor para ser lido a partir da memória.
  2. Reduz o risco de erros de memória em-consistência.

Previne JVM da leitura de valores no registo, e as forças de seus valor a ser lido a partir da memória.

A bandeira ocupado é usado para prevenir um fio de continuar enquanto o dispositivo está ocupado ea bandeira não é protegido por um bloqueio:

while (busy) {
    /* do something else */
}

O segmento de testes continuará quando outro segmento desliga o bandeira ocupado :

busy = 0;

No entanto, uma vez ocupado é acessado com freqüência no segmento de teste, a JVM pode otimizar o teste colocando o valor de ocupado num registo, em seguida, testar o conteúdo do registo sem ler o valor de ocupado na memória antes de cada teste. O fio testando nunca iria ver a mudança ocupado eo outro thread só iria alterar o valor de ocupado na memória, resultando em impasse. Declarando o bandeira ocupado como forças voláteis seu valor para ser lido antes de cada teste.

Reduz o risco de erros de consistência de memória.

Usando variáveis ??voláteis reduz o risco de erros consistência de memória , porque qualquer gravação para uma variável volátil estabelece uma "acontece-antes" relação com leituras subsequentes dessa mesma variável. Isso significa que as alterações em uma variável volátil estão sempre visíveis para outros tópicos.

A técnica de leitura, escrita sem erros de consistência de memória é chamado ação atômica .

Uma ação atômica é aquele que efetivamente acontece tudo de uma vez. Uma ação atômica não pode parar no meio: ou acontece completamente, ou não acontecer. Sem efeitos colaterais de uma ação atômica são visíveis até que a ação seja concluída.

A seguir estão as ações que você pode especificar que são atômica:

  • Lê e escreve são atômicas para as variáveis ??de referência e para a maioria variáveis ??primitivas (todos os tipos, com excepção longa e dupla).
  • Lê e escreve são atômicas para todas as variáveis ??declarou volátil (Incluindo longa e variáveis ??duplas).

Felicidades!

As variáveis ??voláteis são sincronização leve. Quando a visibilidade dos dados mais recentes entre os tópicos é exigência e atomicity pode ser comprometida, em tais situações variáveis ??voláteis devem ser preferidas. Leia sobre as variáveis ??voláteis sempre voltar mais write recente feito por qualquer segmento, uma vez que não são nem em cache nos registos nem em caches onde outros processadores não pode ver. Volátil é Lock-Livre. Eu uso volátil, quando cenário atende aos critérios, como mencionado acima.

Volatile não seguinte.

1> ler e escrever de variáveis ??voláteis por diferentes tópicos são sempre a partir da memória, e não da própria cache ou cpu registo do segmento. Então, cada thread sempre lida com o último valor. 2> Quando 2 tópicos diferentes trabalham com a mesma instância ou variáveis ??estáticas na pilha, pode-se ver outras ações é tão fora de ordem. Veja o blog de Jeremy Manson sobre este assunto. Mas volátil ajuda aqui.

A seguir plenamente em execução mostra o código como uma série de tópicos podem ser executadas em ordem predefinida e imprimir saídas sem o uso de palavras-chave sincronizados.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

Para conseguir isso, pode utilizar o seguinte código de execução de pleno direito.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

O link a seguir github tem um leia-me, o que dá explicação adequada. https://github.com/sankar4git/volatile_thread_ordering

A partir de documentação do Oracle página , a necessidade de volátil variável surge para problemas de consistência de memória correção:

Usando variáveis ??voláteis reduz o risco de erros de consistência de memória, porque qualquer gravação para uma variável volátil estabelece uma acontece-antes de relacionamento com leituras subsequentes dessa mesma variável.

Isto significa que muda a uma variável volatile estão sempre visíveis para outros tópicos. Isso também significa que quando um thread lê uma variável volátil, que vê não apenas o mais recente alteração no volatile, mas também os efeitos colaterais do código que levaram a mudança.

Como explicado na resposta Peter Parker, na ausência de modificador volatile, pilha de cada thread pode ter sua própria cópia da variável. Ao fazer a variável como volatile, problemas de consistência de memória foram corrigidos.

Tenha um olhar em jenkov tutorial página para melhor compreensão.

Tenha um olhar em questão SE relacionada para mais alguns detalhes sobre casos voláteis e uso de usar volátil:

Diferença entre volátil e sincronizado em Java

Um caso de uso prático:

Você tem muitos segmentos, que precisam imprimir o tempo atual em um formato específico, por exemplo: java.text.SimpleDateFormat("HH-mm-ss"). Yon pode ter uma classe, que converte o tempo atual em SimpleDateFormat e atualizado a variável para cada um segundo. Todos os outros tópicos pode simplesmente usar essa variável volátil para imprimir o tempo atual em arquivos de log.

A variável volátil é modificado de forma assíncrona por executando concorrentemente tópicos em uma aplicação Java. Não é permitido ter uma cópia local de uma variável que é diferente do valor atualmente retido na memória "main". Efetivamente, uma variável declarada volátil deve ter seus dados sincronizados em todos os tópicos, de modo que sempre que você acessar ou atualizar a variável em qualquer segmento, todos os outros tópicos ver imediatamente o mesmo valor. Claro, é provável que as variáveis ??voláteis têm um acesso maior e atualização sobrecarga do que as variáveis ??"simples", uma vez que os fios razão pode ter a sua própria cópia de dados é para uma melhor eficiência.

Quando um campo é declarado volátil, o compilador e runtime estão cientes de que esta variável é compartilhada e que as operações sobre ele não deve ser reordenadas com outras variáveis ??operations.Volatile memória não são armazenados em cache em registros ou em esconderijos onde eles estão escondido de outros processadores, por isso, uma leitura de uma variável volátil sempre retorna a gravação mais recente por qualquer segmento.

para referência, enviamos esta http: // techno -terminal.blogspot.in/2015/11/what-are-volatile-variables.html

A chave volátil quando usado com uma variável, irá certificar-se de que tópicos de leitura desta variável vai ver o mesmo valor. Agora, se você tem vários segmentos de leitura e escrita a uma variável, tornando a variável volátil não será suficiente e os dados serão corrompidos. tópicos de imagem têm ler o mesmo valor, mas cada um tem feito algumas chages (digamos incrementado um contador), ao escrever de volta para a memória, a integridade dos dados é violada. É por isso que é necessário para fazer a varible sincronizado (formas diffrent são possíveis)

Se as mudanças são feitas por 1 fio e os outros precisam apenas para ler este valor, a volatilidade será adequado.

Gosto de Jenkov explicação :

O Java volátil palavra-chave é usada para marcar uma variável Java como "sendo armazenados na memória principal". Mais precisamente isso significa, que a cada leitura de uma variável volátil será lido a partir da memória principal do computador, e não a partir do cache da CPU, e que toda a gravação para uma variável volátil será escrito para a memória principal, e não apenas para o cache da CPU .

Na verdade, desde Java 5 as garantias palavra-chave volátil mais do que apenas isso variáveis ??voláteis são gravados e lidos a partir da memória principal.

É garantia de visibilidade estendida chamada acontece-antes de garantia.

Considerações sobre o desempenho de volátil

Leitura e escrita de variáveis ??voláteis faz com que a variável a ser lido ou escrito para a memória principal. Leitura e gravação para a memória principal é mais caro do que acessar o cache da CPU. Acessando variáveis ??voláteis também evitar reordenação de instruções que é uma técnica normal de melhoria de desempenho. Assim, você só deve usar variáveis ??voláteis quando você realmente precisa para fazer valer a visibilidade de variáveis.

variável volátil é basicamente usado para atualização instantânea (rubor) na linha principal cache compartilhado, uma vez que atualizado, de modo que as mudanças refletida para todos os segmentos de trabalho imediatamente.

A seguir é um código muito simples para demonstrar a exigência de volatile para a variável que é usado para controlar a execução da linha de outro segmento (este é um cenário onde é necessária volatile).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

Quando volatile não é usado: você nunca verá ' Stopped on: xxx ' mensagem mesmo após ' Parar em: xxx ' , eo programa continua a funcionar.

Stopping on: 1895303906650500

Quando volatile utilizados: você verá o ' Stopped on: xxx '. Imediatamente

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Demonstração: https://repl.it/repls/SilverAgonizingObjectcode

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top