atualizar a memória antes da sincronização?
-
13-12-2019 - |
Pergunta
é mencionado no modelo de memória Java que:Quando um thread sai de um bloco sincronizado como parte da liberação do monitor associado, o JMM exige que o cache do processador local seja liberado para a memória principal.De forma similar, como parte da aquisição do monitor ao entrar em um bloco sincronizado, os caches locais são invalidados para que as leituras subsequentes irão diretamente para a memória principal e não para o cache local.
então por que nesse código devo declarar a instância como volátil, já que quando o segundo thread entrar no bloco de sincronização irá diretamente para a memória principal?
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
Quero dizer, quando outro thread entra no bloco de sincronização e faz a segunda verificação, ele deve ser atualizado da memória principal, conforme mencionado.
Solução
A condição de corrida é esta:
Tópico A vê
instance == NULL
e está executando este códigoinstance = new MySingleton();
.A escrita parainstance
é visível, mas as gravações emMySingleton
ainda não estão.Tópico B vê
instance != NULL
e começa a trabalhar na instância.Thread B agora está trabalhando em um objeto cuja construção ele não pode ver.
Fazendo instance
volátil resolve o problema, pois a especificação de memória JDK, a partir do JDK5, garante que as gravações em objetos não voláteis não serão vistas fora de ordem em relação a uma gravação em um objeto volátil.Então, qualquer tópico que vê instance != NULL
deve ver a própria instância.
Outras dicas
Você deve declará-lo volátil, caso contrário não há garantia de que duas chamadas para getInstance() retornarão a mesma instância.
Não há garantia de que a memória principal será acessada, apenas um valor consistente com o cache.ou sejatodos os threads verão o mesmo valor.
POR FALAR NISSO:Você sabe, é claro, que é muito mais complexo do que o necessário.Tudo o que você precisa é
public enum MySingleton {
INSTANCE;
}
faz praticamente a mesma coisa.