Java Sincronizou a palavra -chave libera o cache?
-
25-09-2019 - |
Pergunta
Java 5 e acima apenas. Suponha que um computador de memória compartilhada multiprocessador (você provavelmente está usando um agora).
Aqui está um código para a inicialização preguiçosa de um singleton:
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;
}
}
Faz instance
tem que ser declarado volatile
Para impedir que o otimizador reescreva getInstance () da seguinte maneira (que estaria correta em um programa seqüencial):
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
// instance must be null or we wouldn't be here (WRONG!)
instance = new MySingleton();
}
}
}
Supondo que o otimizador não reescreva o código, se instance
não é declarado volatile
ainda é garantido ser liberado na memória quando o synchronized
o bloco é exitado e leia da memória quando o synchronized
O bloco é inserido?
EDIT: Eu esqueci de fazer getInstance () estático. Não acho que isso mude a validade das respostas; Todos vocês sabiam o que eu quis dizer.
Solução
Sim, instance
deve ser declarado volatile
. Mesmo assim, é aconselhável não usar o bloqueio verificado duas vezes. Ele (ou para ser preciso, o modelo de memória Java) costumava ter uma falha séria que permitia a publicação de objetos parcialmente implementados. Isso foi corrigido no Java5, ainda é um idioma obsoleto e não há mais necessidade de usá -lo - use o Idioma do titular de inicialização preguiçoso em vez de.
A partir de Concorrência de java na prática, Seção 16.2:
O verdadeiro problema com a DCL é a suposição de que a pior coisa que pode acontecer ao ler uma referência de objeto compartilhado sem sincronização é ver erroneamente um valor obsoleto (neste caso,
null
); Nesse caso, o idioma DCL compensa esse risco, tentando novamente com a fechadura mantida. Mas o pior caso é realmente consideravelmente pior - é possível ver um valor atual da referência, mas valores obsoletos para o estado do objeto, o que significa que o objeto pode ser visto em um estado inválido ou incorreto.Mudanças subsequentes no JMM (Java 5.0 e posterior) permitiram que o DCL funcionasse E se
resource
é feitovolatile
, e o impacto de desempenho disso é pequeno desdevolatile
As leituras geralmente são apenas um pouco mais caras do que as leituras não voláteis. No entanto, este é um idioma cuja utilidade passou amplamente - as forças que o motivaram (sincronização não contendida lenta, startup lenta da JVM) não estão mais em jogo, tornando -o menos eficaz como uma otimização. O Idiom do titular de inicialização preguiçoso oferece os mesmos benefícios e é mais fácil de entender.
Outras dicas
Sim, a instância precisa ser volátil usando bloqueio verificado duas vezes em Java, porque, caso contrário, a inicialização de Mysingleton poderia expor um objeto parcialmente construído para o restante do sistema. Também é verdade que os threads sincronizam quando chegarem à declaração "sincronizada", mas isso é tarde demais neste caso.
Wikipedia E algumas outras perguntas sobre o Stack Overflow têm boas discussões sobre o "bloqueio verificado duas vezes", então eu aconselho a ler sobre isso. Eu também aconselho não usá -lo, a menos que o perfil mostre uma necessidade real de desempenho nesse código específico.
Há um idioma mais seguro e mais legível para a inicialização preguiçosa dos singletons Java:
class Singleton {
private static class Holder {
static final Singleton instance = create();
}
static Singleton getInstance() {
return Holder.instance;
}
private Singleton create() {
⋮
}
private Singleton() { }
}
Se você usar o padrão de bloqueio verificado mais detalhado, você deve declarar o campo volatile
, como outros já observaram.
Não. Não precisa ser volátil. Ver Como resolver a declaração "bloqueio verificado duas vezes está quebrado" em Java?
Tentativas anteriores falharam porque, se você pode trapacear o Java e evitar o volátil/sincronizar, o Java poderá enganá -lo e fornecer uma visão incorreta do objeto. No entanto, o novo final
A semântica resolve o problema. Se você recebe uma referência de objeto (por leitura comum), pode ler com segurança o seu final
Campos.