Aprendizado de padrão de design 2: padrão singleton

padrão singleton

O chamado padrão de projeto singleton de uma classe é usar um determinado método para garantir que em todo o sistema de software, apenas uma instância de objeto possa existir para uma determinada classe, e a classe fornece apenas um método (método estático) para obter seu instância do objeto.
Como o SessionFactory do Hibernate, que atua como um proxy para a fonte de armazenamento de dados e é responsável por criar objetos Session. SessionFactory não é leve, em geral, um projeto normalmente só precisa de uma SessionFactory, que é o padrão singleton.

Existem oito maneiras de usar o padrão singleton:

  1. Estilo chinês com fome (constante estática)
  2. Estilo chinês faminto (bloco de código estático)
  3. Preguiçoso (thread inseguro)
  4. Lazy (métodos seguros para threads e sincronizados)
  5. Lazy (blocos de código sincronizados e seguros para threads)
  6. dupla verificação
  7. classe interna estática
  8. enumerar

Chinês faminto

1.
Exemplo de aplicação de constante estática em chinês com fome (constante estática) As
etapas são as seguintes:

  1. Privatização do construtor (para evitar acesso externo ao construtor dentro da classe, ou seja, você não pode usar a palavra-chave new para criar objetos)
  2. Objetos criados dentro da classe
  3. Exponha um método público estático. getInstance
  4. Código

exemplo:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//饿汉式(使用静态变量实现)
class SingleTon{
    
    
    //1、构造器私有化,对外不提供new
    private SingleTon(){
    
    
    }

    //2、本类内部创建对象实例
    private final static SingleTon instance = new SingleTon();

    //3、提供一个公有的静态方法,返回实例对象
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

public class Type1 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);//结果是true,证明两个对象相等
    }
}

Análise das vantagens e desvantagens
Vantagens: Este método de escrita é relativamente simples, ou seja, a instanciação é concluída quando a classe é carregada. Evita problemas de sincronização de threads .

Desvantagens: A instanciação é concluída quando a classe é carregada, o que não atinge o efeito de Lazy Loading . Se a instância nunca for usada do início ao fim, causará um desperdício de memória.

Este método evita o problema de sincronização de várias threads com base no mecanismo ClassLoder. No entanto, a instância é instanciada quando a classe é carregada. No modo singleton, a maioria delas chama o método getInstance. No entanto, existem muitos motivos para o carregamento da classe, portanto é impossível Certificar-se de que existem outras maneiras (ou outros métodos estáticos) de causar carregamento de classe.Neste momento, inicializar a instância não obterá o efeito de carregamento lento.

Conclusão: Este padrão singleton está disponível e pode causar desperdício de memória


2. Exemplo de bloco de código estático :

//饿汉式(使用静态代码块实现)
class SingleTon{
    
    

    private SingleTon(){
    
    
    }

    private static SingleTon instance;

    //在静态代码块中创建单例对象
    static {
    
    
        instance = new SingleTon();
    }
    
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

Análise de vantagens e desvantagens
Este método é semelhante ao método acima, exceto que o processo de instanciação da classe é colocado no bloco de código estático. Quando a classe é carregada, o código no bloco de código estático é executado para inicializar a instância do aula. Os prós e contras são os mesmos acima.

Conclusão: Este padrão singleton está disponível, mas pode causar desperdício de memória

preguiçoso


1. Exemplo de gravação insegura para thread :

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//懒汉式(线程不安全)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static SingleTon getInstance(){
    
    
        if (instance==null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Análise de vantagens e desvantagens
Vantagens: Jogou o efeito de Lazy Loading.

Desvantagem: Ele só pode ser usado em um único thread . Se um thread entrar no bloco de instrução de julgamento if (singleton == null) em multi-threading, ele não terá tempo para executá-lo, e outro thread também passará por esse comando de julgamento. Neste momento, várias instâncias serão geradas.

Conclusão: No desenvolvimento real, não use este método

2. Métodos sincronizados e seguros para thread
Exemplo:

//懒汉式(线程安全,同步方法)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //加入了同步方法,解决了线程不安全的问题
    public static synchronized SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Análise de vantagens e desvantagens
Vantagens: resolve o problema de insegurança de thread

Desvantagens: A eficiência é muito baixa, quando cada thread deseja obter uma instância da classe, precisa sincronizar a execução do método getInstance(). Na verdade, este método é suficiente para executar o código de instanciação apenas uma vez, se você quiser obter uma instância dessa classe posteriormente, basta retorná-la diretamente. O método é muito ineficiente para sincronização

Conclusão: No desenvolvimento real, este método não é recomendado.

3. Encadeie blocos de código inseguros e sincronizados
Exemplo:

//懒汉式(线程不安全,同步代码块)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }
    
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                instance = new SingleTon();
            }
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Análise das vantagens e desvantagens
Este método visa melhorar o quarto método de implementação, pois a eficiência do método de sincronização anterior é muito baixa e é alterado para sincronizar para gerar blocos de código instanciados.
Mas essa sincronização não desempenha o papel de sincronização de threads! ! ! Consistente com a situação encontrada no terceiro método de implementação, se um thread entrar no bloco de julgamento if (singleton == null) e ainda não tiver tido tempo de executá-lo, outro thread também passará na instrução de julgamento, então ele gerará várias instâncias

Conclusão: No desenvolvimento real, este método não pode ser utilizado.

dupla verificação

exemplo:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//双重检查(线程安全,线程同步)
class SingleTon{
    
    
	//volatile使instance变量的状态在各个线程之间是可见的
    private static volatile SingleTon instance;

    private SingleTon(){
    
    }

    //解决线程安全的问题,同时解决懒加载的问题,同时解决懒加载问题
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                if (instance == null){
    
    
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}
public class Type3 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

De acordo com a análise de código acima, se três encadeamentos a, b e c entrarem no método getInstance(), os encadeamentos a e b inserem a primeira instrução de julgamento if (instance == null) juntos, e o encadeamento a é mais avançado que o encadeamento b . Insira primeiro o bloco de código de sincronização. Neste momento, como apenas um thread tem permissão para executar no bloco de código de sincronização, o thread a primeiro inicializa a instância e, em seguida, sai do bloco de código de sincronização; neste momento, como o thread a sai do bloco de código de sincronização, então a thread b pode entrar no bloco de código de sincronização. Após a thread b entrar no bloco de código de sincronização, uma vez que a thread a já inicializou a instância, a thread b não inicializará a instância novamente e sairá diretamente o bloco de código de sincronização; Foi inicializado, portanto, não entrou na instrução de julgamento.

Análise de vantagens e desvantagens
Vantagens: segurança de rosca, carregamento lento, alta eficiência

O conceito de Double-Check é frequentemente usado no desenvolvimento multi-thread.Como mostrado no código acima, realizamos duas verificações if (singleton == null) para garantir a segurança do thread.
Dessa forma, o código de instanciação precisa ser executado apenas uma vez, e quando for acessado novamente posteriormente, julgue se (singleton == null), retorne diretamente o objeto instanciado e evite a repetição de sincronização de métodos.

Conclusão: No desenvolvimento real, este padrão de projeto singleton é recomendado.

classe interna estática

exemplo:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//静态内部类实现
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //写一个静态内部类,该类中有一个静态属性
    private static class SingleTonInstance{
    
    
        private static final SingleTon INSTANCE = new SingleTon();
    }

    //提供一个公有方法,直接返回SingleTonInstance.INSTANCE
    public static SingleTon getInstance(){
    
    
        return SingleTonInstance.INSTANCE;
    }
}

public class Type4 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Análise de código, escrevemos uma classe interna estática SingleTonInstance em SingleTon, SingleTonInstance não será carregado imediatamente quando a classe SingleTon for carregada, e a classe SingleTonInstance não será carregada até que a propriedade estática INSTANCE na classe SingleTonInstance seja chamada no método getInstance(). é carregado para obter o efeito de carregamento lento e o processo de carregamento de classe JVM é seguro para threads.

Análise das vantagens e desvantagens
Esse método adota o mecanismo de carregamento de classes para garantir que haja apenas uma thread quando a instância for inicializada.
O método da classe interna estática não será instanciado imediatamente quando a classe Singleton for carregada, mas quando for necessário instanciar, o método getInstance será chamado e a classe SingletonInstance será carregada, completando assim a instanciação do Singleton.
As propriedades estáticas de uma classe são inicializadas apenas quando a classe é carregada pela primeira vez, então aqui, a JVM nos ajuda a garantir a segurança do encadeamento.Quando a classe é inicializada, outros encadeamentos não podem entrar.

Vantagens: evite a insegurança de thread, use recursos de classe interna estática para obter carregamento atrasado, alta eficiência

Conclusão: Recomendado para uso.

enumerar

exemplo:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//枚举(使用枚举也可以实现单例)
enum SingleTon{
    
    
    INSTANCE;   //属性
    public void sayOK(){
    
    
        System.out.println("ok~");
    }
}

public class Type5 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.INSTANCE;
        SingleTon instance2 = SingleTon.INSTANCE;
        System.out.println(instance == instance2);
    }
}

Análise de vantagens e desvantagens
Isso implementa o padrão singleton com a ajuda da enumeração adicionada no JDK1.5. Não apenas evita problemas de sincronização multi-thread, mas também evita que a desserialização recrie novos objetos .

Essa abordagem é defendida pelo autor do Effective Java, Josh Bloch

Conclusão: Recomendado para uso

Aplicação do padrão singleton no JDK

No JDK, java.lang.Runtime é o modo singleton clássico (estilo chinês faminto). Vamos dar uma olhada em seu código-fonte, conforme mostrado abaixo:
insira a descrição da imagem aqui

Resumir

  1. O modo singleton garante que apenas um objeto dessa classe exista na memória do sistema, economizando recursos do sistema. Para alguns objetos que precisam ser criados e destruídos com frequência, o uso do modo singleton pode melhorar o desempenho do sistema
  2. Quando você deseja instanciar uma classe singleton, você deve se lembrar de usar o método correspondente para obter o objeto, não o novo
  3. Cenários usados ​​pelo padrão singleton: objetos que precisam ser criados e destruídos com frequência, demoram ou consomem recursos para criar objetos (ou seja: objetos pesados), mas objetos usados ​​com frequência, objetos de ferramentas, objetos frequentes Objetos que acessam bancos de dados ou arquivos (como fontes de dados, fábricas de sessão, etc.)


PS: Você também pode ir ao meu blog pessoal para ver mais conteúdo

Endereço do blog pessoal: blog do colega de classe Xiaoguan

Acho que você gosta

Origin blog.csdn.net/weixin_45784666/article/details/120275606
Recomendado
Clasificación