O que é um singleton em Java?
Singleton é um dos padrões de design de nível de classe mais simples. Às vezes as pessoas dizem "esta classe é singleton", o que significa que a classe implementa o padrão de design singleton. Às vezes é necessário escrever uma classe onde restringimos a instanciação a um único objeto. Por exemplo, uma classe responsável por registrar ou conectar a um banco de dados. O padrão de design singleton descreve como podemos conseguir isso. Um singleton é um padrão de design que faz duas coisas:-
Ele garante que sempre haverá apenas uma instância da classe.
-
Ele fornece um único ponto de acesso global a essa instância.
-
Um construtor privado. Isso limita a capacidade de criar objetos da classe fora da própria classe.
-
Um método estático público que retorna a instância da classe. Esse método é chamado getInstance . Este é o ponto de acesso global à instância da classe.
Opções de implementação
O padrão de projeto singleton é aplicado de várias maneiras. Cada opção é boa e ruim à sua maneira. Como sempre, não existe uma opção perfeita aqui, mas devemos nos esforçar para encontrar uma. Em primeiro lugar, vamos decidir o que é bom e ruim e quais métricas afetam como avaliamos as várias implementações do padrão de design. Vamos começar com o bom. Aqui estão os fatores que tornam uma implementação mais suculenta e atraente:-
Inicialização preguiçosa: a instância não é criada até que seja necessária.
-
Código simples e transparente: essa métrica, claro, é subjetiva, mas é importante.
-
Segurança de thread: operação correta em um ambiente multithread.
-
Alto desempenho em um ambiente multithread: pouco ou nenhum bloqueio de thread ao compartilhar um recurso.
-
Sem inicialização preguiçosa: quando a classe é carregada quando o aplicativo é iniciado, independentemente de ser necessário ou não (paradoxalmente, no mundo da TI é melhor ser preguiçoso)
-
Código complexo e difícil de ler. Essa métrica também é subjetiva. Se seus olhos começarem a sangrar, presumiremos que a implementação não é a melhor.
-
Falta de segurança do fio. Em outras palavras, "perigo de discussão". Operação incorreta em um ambiente multiencadeado.
-
Baixo desempenho em um ambiente multiencadeado: os encadeamentos bloqueiam uns aos outros o tempo todo ou frequentemente ao compartilhar um recurso.
Código
Agora estamos prontos para considerar várias opções de implementação e indicar os prós e contras:Simples
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
A implementação mais simples. Prós:
-
Código simples e transparente
-
Segurança do fio
-
Alto desempenho em um ambiente multi-threaded
- Nenhuma inicialização preguiçosa.
Inicialização preguiçosa
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Prós:
-
Inicialização preguiçosa.
-
Não thread-safe
acesso sincronizado
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Prós:
-
Inicialização preguiçosa.
-
Segurança do fio
-
Baixo desempenho multithread
Bloqueio duplamente verificado
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Prós:
-
Inicialização preguiçosa.
-
Segurança do fio
-
Alto desempenho em um ambiente multi-threaded
-
Não suportado em versões anteriores do Java abaixo de 1.5 (o uso da palavra-chave volátil é corrigido desde a versão 1.5)
titular da classe
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
Prós:
-
Inicialização preguiçosa.
-
Segurança do fio.
-
Alto desempenho em um ambiente multi-threaded.
-
A operação correta requer uma garantia de que o objeto singleton seja inicializado sem erros. Caso contrário, a primeira chamada para o método getInstance resultará em um ExceptionInInitializerError e todas as chamadas subsequentes produzirão um NoClassDefFoundError .
Implementação | Inicialização preguiçosa | Segurança do fio | Desempenho multithread | Quando usar? |
---|---|---|---|---|
Simples | - | + | Rápido | Nunca. Ou possivelmente quando a inicialização preguiçosa não é importante. Mas nunca seria melhor. |
Inicialização preguiçosa | + | - | Não aplicável | Sempre quando multithreading não é necessário |
acesso sincronizado | + | + | Lento | Nunca. Ou possivelmente quando o desempenho multithread não importa. Mas nunca seria melhor. |
Bloqueio duplamente verificado | + | + | Rápido | Em casos raros, quando você precisa lidar com exceções ao criar o singleton (quando o singleton titular da classe não é aplicável) |
titular da classe | + | + | Rápido | Sempre que multithreading for necessário e houver garantia de que o objeto singleton será criado sem problemas. |
Prós e contras do padrão singleton
Em geral, um singleton faz exatamente o que se espera dele:-
Ele garante que sempre haverá apenas uma instância da classe.
-
Ele fornece um único ponto de acesso global a essa instância.
-
Um singleton viola o princípio de responsabilidade única: além de suas funções diretas, a classe singleton também controla o número de instâncias.
-
A dependência de uma classe comum de um singleton não é visível no contrato público da classe.
-
Variáveis globais são ruins. Em última análise, um singleton se transforma em uma variável global pesada.
-
A presença de um singleton reduz a capacidade de teste do aplicativo como um todo e das classes que usam o singleton em particular.
GO TO FULL VERSION