No processamento simultâneo de thread Java, atualmente há muita confusão sobre o uso da palavra-chave volátil. Pensa-se que usando essa palavra-chave, tudo ficará bem ao executar o processamento simultâneo multithread.
A linguagem Java suporta multithreading. Para resolver o problema de simultaneidade de threads, o bloco sincronizado e os mecanismos de palavras-chave voláteis são introduzidos dentro da linguagem.
sincronizado
Todos estão familiarizados com blocos sincronizados, que são implementados por meio da palavra-chave sincronizada. Com a adição de instruções sincronizadas e de bloco, quando acessado por vários threads, apenas um thread pode usar métodos modificados sincronizados ou blocos de código ao mesmo tempo.
volátil
Para variáveis modificadas com volátil, toda vez que o thread usar a variável, ele lerá o valor modificado da variável. Volátil pode facilmente ser mal utilizado para realizar operações atômicas.
Vejamos um exemplo abaixo. Implementamos um contador Cada vez que o thread é iniciado, o método counter inc será chamado para adicionar um ao contador.
Ambiente de execução - versão jdk: jdk1.6.0_31, memória: 3G cpu: x86 2.4G
Copie o código do código da seguinte forma:
classe pública Contador {
contagem interna estática pública = 0;
public static void inc() {
//Atraso 1 milissegundo aqui para tornar o resultado óbvio
tentar {
Thread.sleep(1);
} catch (InterruptedException e) {
}
contar++;
}
public static void main(String[] args) {
//Inicie 1000 threads ao mesmo tempo para realizar cálculos i++ e ver os resultados reais
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Substituir
execução void pública() {
Contador.inc();
}
}).começar();
}
//O valor aqui pode ser diferente cada vez que for executado, possivelmente 1000
System.out.println("Resultado da execução: Counter.count=" + Counter.count);
}
}
Resultado da execução: Counter.count=995
O resultado real da operação pode ser diferente a cada vez. O resultado desta máquina é: resultado da execução: Counter.count = 995. Pode-se observar que em um ambiente multithread, Counter.count não espera que o resultado seja 1000.
Muitas pessoas pensam que este é um problema de simultaneidade multi-thread. Você só precisa adicionar volátil antes da contagem da variável para evitar esse problema.
Copie o código do código da seguinte forma:
classe pública Contador {
contagem int estática pública volátil = 0;
public static void inc() {
//Atraso 1 milissegundo aqui para tornar o resultado óbvio
tentar {
Thread.sleep(1);
} catch (InterruptedException e) {
}
contar++;
}
public static void main(String[] args) {
//Inicie 1000 threads ao mesmo tempo para realizar cálculos i++ e ver os resultados reais
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Substituir
execução void pública() {
Contador.inc();
}
}).começar();
}
//O valor aqui pode ser diferente cada vez que for executado, possivelmente 1000
System.out.println("Resultado da execução: Counter.count=" + Counter.count);
}
}
Resultado da execução: Counter.count=992
O resultado final ainda não é 1000 como esperávamos. Vamos analisar os motivos abaixo.
No artigo Java Garbage Collection, a alocação de memória durante o tempo de execução da JVM é descrita. Uma das áreas de memória é a pilha da máquina virtual jvm. Cada thread possui uma pilha de threads quando está em execução. Quando um thread acessa o valor de um objeto, ele primeiro encontra o valor da variável correspondente à memória heap por meio da referência do objeto e, em seguida, carrega o valor específico da variável de memória heap na memória local do thread para criar uma cópia do Depois disso, o thread não tem nada a ver com o valor da variável do objeto na memória heap. Em vez disso, ele modifica diretamente o valor da variável de cópia em um determinado momento após a modificação (antes da saída do thread). o valor da cópia da variável de thread é automaticamente gravado na variável do objeto no heap. Dessa forma, o valor do objeto no heap muda. A imagem abaixo
Descreva esta interação
ler e carregar copia variáveis da memória principal para a memória de trabalho atual
usar e atribuir código de execução e alterar valores de variáveis compartilhadas
armazenar e gravar conteúdo relacionado à memória principal com dados da memória de trabalho
onde usar e atribuir podem aparecer várias vezes
Porém, essas operações não são atômicas. Ou seja, após o carregamento da leitura, se a variável de contagem da memória principal for modificada, o valor na memória de trabalho do thread não será alterado porque foi carregado, portanto o resultado calculado será o esperado. o mesmo
Para variáveis modificadas por voláteis, a máquina virtual JVM apenas garante que o valor carregado da memória principal para a memória de trabalho do thread seja o mais recente
Por exemplo, se o thread 1 e o thread 2 descobrirem que o valor de contagem na memória principal é 5 durante as operações de leitura e carregamento, eles carregarão o valor mais recente.
Depois que a contagem de heap do thread 1 for modificada, ela será gravada na memória principal e a variável de contagem na memória principal se tornará 6.
Como o thread 2 já executou operações de leitura e carregamento, após realizar a operação, ele também atualizará o valor da variável de contagem da memória principal para 6.
Como resultado, depois que dois threads forem modificados no tempo usando a palavra-chave volátil, ainda haverá simultaneidade.