Verifique o bloqueio / verifique o bloqueio O julgamento anti-aéreo de camada dupla tem sido problemático por um longo tempo. Instância
public class Singleton {
private volatile static Singleton singleton;
//私有构造函数避免调用
private Singleton (){}
public static Singleton getSingleton() {
// 先判断对象是否创建过
if (singleton == null) {
//类对象加锁
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
//字节码层
//JIT CPU 可能对如下指令进行重排序
// 1.分配空间
// 2.初始化
// 3.引用赋值
如果指令重排后指令如下:
//1.分配空间
//3.引用赋值 如果在当前指令执行完后,有其他线程获取到实例,将拿到未初始化的实例;
//2. 初始化
}
}
}
return singleton;
}
}
Explicação: Quando A e B chamam getSingleton ao mesmo tempo, considera-se que o primeiro if está vazio. Nesse momento, A obtém o bloqueio e executa o segundo nível de julgamento se. A condição é estabelecida e um objeto é novo;
B espera na camada externa, A é criado, o bloqueio é liberado, B obtém o bloqueio e o segundo nível se o julgamento é executado. A condição não é estabelecida e o bloqueio é liberado. Quando C chama getSingleton, o primeiro nível de julgamento não é estabelecido e o objeto singleton é retornado diretamente para evitar entrar no bloqueio e reduzir a sobrecarga de desempenho.
Compreensão adicional: dois deles estão vazios, o primeiro vazio é para reduzir o número de vezes para entrar no bloco de código de sincronização no caso de multithreading, e o segundo vazio é para evitar multithreading (A, B no caso de dois threads) , B chama o método getSingleton ao mesmo tempo e, ao mesmo tempo, entra na primeira camada if (singleton == null) {}. Em condições de corrida, se A obtém o bloqueio do objeto, entra no bloco de código síncrono, e B blocos esperando, esperando Depois que o objeto de instância é criado e o bloqueio é liberado, B entra no bloco de código de sincronização, mas neste momento o segundo nível se (singleton == null) {} julga que o singleton não está vazio e retorna o singleton diretamente );
Resumo: há duas instruções para determinar se ele está vazio. A primeira vez é para melhorar a eficiência e evitar a execução de blocos de código sincronizados a cada vez. A segunda vez é para evitar a insegurança causada pelo multithreading. Quando dois threads julgam que o primeiro está vazio ao mesmo tempo, eles entrarão no bloco de código de sincronização um após o outro. Neste momento, se não houver uma segunda condição de vazio, várias instâncias serão criadas.
palavras-chave volite:
É necessário usar modificação de palavra-chave volátil
Na verdade, esse código é dividido em três etapas:
singleton = novo Singleton ();
- Alocar espaço de memória para singleton
- Inicializar singleton
- Aponte o singleton para o endereço de memória alocado.
No entanto, jvm tem as características de rearranjo de instruções e a ordem de execução pode mudar, não na ordem de 123, mas pode ser de 132, o que fará com que um thread obtenha uma instância não inicializada
, como : t1 é executado 13. Neste momento, depois que t2 chama getInstance (), verifica-se que o singleton não está mais vazio, então o singleton é retornado, mas neste momento, o singleton não foi inicializado
volátil e o rearranjo da instrução jvm pode ser proibido para garantir que pode ser normal em um ambiente multithread executado
Quer saber mais: Recomendar
Compreensão aprofundada do princípio de implementação sincronizada da simultaneidade Java
https://blog.csdn.net/javazejian/article/details/72828483
referência: