Segurança de encadeamento em classes Java Singleton
Singleton é um dos padrões de design criacional mais amplamente utilizado para restringir o objeto criado por aplicativos. Se você estiver usando em um ambiente multiencadeado, a segurança de encadeamento da classe singleton é muito importante. Em aplicativos do mundo real, recursos como conexões de banco de dados ou Enterprise Information Systems (EIS) são limitados e devem ser usados com sabedoria para evitar qualquer escassez de recursos. Para conseguir isso, podemos implementar uma classe wrapper para o recurso e limitar o número de objetos criados em tempo de execução a um.
Thread Safe Singleton em Java
- Crie o construtor privado para evitar a criação de qualquer novo objeto com o novo operador.
- Declare uma instância estática privada da mesma classe.
- Forneça um método estático público que retornará a variável de instância da classe singleton. Se a variável não for inicializada, inicialize-a ou simplesmente retorne a variável de instância.
Usando as etapas acima, criei uma classe singleton que se parece com a seguinte. ASingleton.java
package com.journaldev.designpatterns;
public class ASingleton {
private static ASingleton instance = null;
private ASingleton() {
}
public static ASingleton getInstance() {
if (instance == null) {
instance = new ASingleton();
}
return instance;
}
}
No código acima, o método getInstance() não é thread-safe. Múltiplos threads podem acessá-lo ao mesmo tempo. Para os primeiros threads quando a variável de instância não é inicializada, vários threads podem entrar no loop if e criar várias instâncias. Isso interromperá nossa implementação de singleton.
Como obter segurança de thread na classe Singleton?
Existem três maneiras pelas quais podemos alcançar a segurança da rosca.
- Crie a variável de instância no momento do carregamento da classe. Prós:
- Segurança de thread sem sincronização
- Fácil de implementar
Contras:
- Criação antecipada de recurso que pode não ser usado no aplicativo.
- O aplicativo cliente não pode passar nenhum argumento, então não podemos reutilizá-lo. Por exemplo, ter uma classe singleton genérica para conexão de banco de dados onde o aplicativo cliente fornece propriedades de servidor de banco de dados.
- Sincronize o método getInstance(). Prós:
- A segurança da linha é garantida.
- O aplicativo cliente pode passar parâmetros
- Inicialização lenta alcançada
Contras:
- Desempenho lento devido à sobrecarga de bloqueio.
- Sincronização desnecessária que não é necessária depois que a variável de instância é inicializada.
- Use bloco sincronizado dentro do loop if e variável volátil Prós:
- A segurança do fio é garantida
- O aplicativo cliente pode passar argumentos
- Inicialização lenta alcançada
- A sobrecarga de sincronização é mínima e aplicável apenas para os primeiros threads quando a variável é nula.
Contras:
- Extra se condição
Olhando para todas as três maneiras de obter thread-safe, acho que a terceira é a melhor opção. Nesse caso, a classe modificada ficará assim:
package com.journaldev.designpatterns;
public class ASingleton {
private static volatile ASingleton instance;
private static Object mutex = new Object();
private ASingleton() {
}
public static ASingleton getInstance() {
ASingleton result = instance;
if (result == null) {
synchronized (mutex) {
result = instance;
if (result == null)
instance = result = new ASingleton();
}
}
return result;
}
}
A variável local result
parece desnecessária. Mas, está lá para melhorar o desempenho do nosso código. Nos casos em que a instância já está inicializada (na maioria das vezes), o campo volátil é acessado apenas uma vez (devido a \retornar resultado; ao invés de \retornar instância;). Isso pode melhorar o desempenho geral do método em até 25%. Se você acha que há maneiras melhores de conseguir isso ou se a segurança do thread está comprometida na implementação acima, comente e compartilhe com todos nós.
Dica bônus
segurança de encadeamento em java.
Você pode conferir mais exemplos de Java em nosso repositório GitHub.