English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Explicação detalhada do padrão de design Singleton no Android

Este artigo descreve o padrão Singleton de programação Android. Compartilho com vocês para referência, conforme a seguir:

Primeiro, introdução

O padrão Singleton é um dos mais amplamente aplicados, pode ser o único padrão de design que muitos engenheiros iniciantes conhecem. Ao aplicar esse padrão, a classe do objeto singleton deve garantir que exista apenas uma instância. Muitas vezes, todo o sistema precisa ter um objeto global, o que é benéfico para a coordenação do comportamento geral do sistema.

Segundo, definição

Garante que uma classe tenha apenas uma instância e se auto-inicialize, fornecendo essa instância para todo o sistema.

Terceiro, cenários de uso

Cenários em que se garante que um tipo de classe tenha apenas um objeto, evitando que sejam consumidos muitos recursos ou que um tipo de objeto deva ter apenas um. Por exemplo, criar um objeto que consome muitos recursos, como acessar IO e banco de dados, neste caso, deve-se considerar o uso do padrão Singleton.

Quarto, métodos de implementação

1、模式饿汉

Código de exemplo:

/**
 * 模式饿汉
 */
public class Singleton {
  Padrão de singleton com双重检查锁定(DCL)
  private Singleton(){}
  public static Singleton getInstance(){
    private static Singleton instance;
      if(instance == null) {
    }
    return instance;
  }
}

Vantagens:Carregamento atrasado (carregar apenas quando necessário)

Desvantagens:Insegura em threads, facilmente ocorre situações de des sincronização em múltiplas threads, como operações de leitura e escrita frequentes em objetos de banco de dados.

2、模式懒汉

Código de exemplo:

/**
 * 模式懒汉
 */
public class Singleton {
  Padrão de singleton com双重检查锁定(DCL)
  private Singleton(){}
  public static synchronized Singleton getInstance(){
    private static Singleton instance;
      if(instance == null) {
    }
    return instance;
  }
}

与饿汉模式相比,getInstance()方法中添加了synchronized关键字,也就是说getInstance是一个同步方法,这就在多线程的情况下保证单例对象的唯一性的手段。但是,细想一下,大家可能会发现一个问题,即使instance已经被初始化(第一次调用的时候就会被初始化instance),每次调用getInstance方法都会进行同步,这样会消耗不必要的资源,这也是懒汉模式存在的最大问题。

Vantagens: Comparado com o padrão hungry汉模式, adiciona a palavra-chave synchronized no método getInstance(), isso significa que getInstance é um método sincronizado, isso garante o único da instância singleton em condições de multi-threading. Mas, se pensarmos bem, podemos descobrir um problema, mesmo que instance já tenha sido inicializado (a instância instance será inicializada pela primeira vez), cada chamada do método getInstance realiza sincronização, isso consumirá recursos desnecessários, que é o maior problema do padrão lazy汉模式.

Desvantagens: Resolve o problema de segurança de thread.

Suplemento:A primeira vez que é carregado, é necessário inicializar a instância imediatamente, a resposta é um pouco lenta, o maior problema é que cada chamada de getInstance realiza sincronização, causando overhead de sincronização desnecessária.

3:No código-fonte do Android, métodos singleton usados incluem InputMethodManager, AccessibilityManager etc., todos usam este padrão singleton

Código de exemplo:

/**
 * 、Double Check Lock(DCL)双重检查锁定
 */
public class Singleton {
  Padrão de singleton com双重检查锁定(DCL)
  private Singleton(){}
  public static Singleton getInstance(){
    private static Singleton instance;
      if(instance == null){
        synchronized (Singleton.class) {
          if(instance == null) {
        }
      }
    }
    return instance;
  }
}

instance = new Singleton();

A característica brilhante deste programa está naturalmente na método getInstance, podemos ver que o método getInstance faz duas verificações de nulo em instance: a primeira camada de verificação é para evitar sincronização desnecessária, a segunda camada de verificação é para criar a instância em caso de null.3Suponha que a thread A execute a instrução instance = new Singleton(), aqui parece ser uma linha de código, mas na verdade não é uma operação atômica, esta linha de código será compilada em várias instruções de assembly, que basicamente faz

)chama o construtor Singleton(), inicializa os campos de membro;1um evento:

)chama o construtor Singleton(), inicializa os campos de membro;2)aloca memória para uma instância Singleton;

)chama o construtor Singleton(), inicializa os campos de membro;3(

)aponta o objeto instance para o espaço de memória alocado (neste momento, instance não é null).1.5No entanto, devido ao compilador Java permitir a execução desordenada do processador, bem como ao JDK1-2-3também pode ser1-3-2Se for o último, e em3Concluído、2Antes de ser executado, é transferido para a thread B, neste momento, devido ao fato de que instance já foi executado no thread A no terceiro ponto, instance já não é nulo, então a thread B pega diretamente instance, e quando for usado novamente, ocorrerá um erro, isso é o problema de falha de DCL, e esse erro difícil de rastrear e reproduzir pode ocultar por muito tempo.

No JDK1.5Depois disso, a SUN oficial já notou esse problema, ajustou o JVM e especificou a palavra-chave volatile, portanto, se o JDK é1.5ou versões posteriores, é necessário alterar a definição de instance para private volatile static Singleton instance para garantir que o objeto instance seja lido a cada vez a partir da memória principal, permitindo usar a escrita DCL para o padrão singleton. Claro, o volatile pode afetar um pouco o desempenho, mas considerando a correção do programa, é ainda assim digno de sacrificar esse pouco de desempenho.

Vantagens:Alta eficiência de uso de recursos, o objeto singleton é instanciado apenas quando executado getInstance pela primeira vez, o que é eficiente. Em cenários de baixa concorrência e baixa segurança, o padrão singleton pode funcionar perfeitamente.

Desvantagens:A primeira carga pode ser um pouco lenta, e também pode falhar ocasionalmente devido ao modelo de memória do Java. Também há certas deficiências em ambientes de alta concorrência, embora a probabilidade seja muito pequena.

Suplemento:No projeto de código aberto de imagem do Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)usando essa maneira.
O padrão DCL é a maneira mais usada de implementar singleton, ele pode instanciar o objeto singleton conforme necessário e garantir a unicidade do objeto singleton na maioria dos cenários, a menos que seu código esteja em cenários concorrentes complexos ou inferiores ao JDK6versão usar, caso contrário, geralmente pode atender às necessidades.

4、Padrão de singleton de classe interna estática

DCL, embora em certa medida resolva problemas como consumo de recursos, sincronização desnecessária e segurança de threads, ainda assim, pode falhar em algumas situações. Este problema é conhecido como falha de double-checked locking (DCL), que é discutido no final do livro 'Prática de Programação Concorrente em Java', e é mencionado que essa 'otimização' é feia e não é recomendada. Em vez disso, é sugerido usar o seguinte código:

Código de exemplo:

/**
 * Padrão de singleton de classe interna estática
 */
public class Singleton {
  private Singleton(){}
  public static Singleton getInstance(){
    return SingletonHolder.instance;
  }
  /**
   * Classe interna estática
   * Carregamento diferido, redução do consumo de memória
   */
  private static class SingletonHolder{}}
    private static final Singleton instance = new Singleton();
  }
}

Quando a classe Singleton é carregada pela primeira vez, a instância não é inicializada, apenas ao chamar o método getInstance da Singleton pela primeira vez que a instância é inicializada. Portanto, a primeira chamada ao método getInstance causará o carregamento da classe SingletonHolder pelo virtural machine, essa maneira não apenas garante a segurança da thread, mas também garante a unicidade do objeto singleton, além de atrasar a instância do singleton, portanto, essa é a maneira de implementação do padrão singleton recomendada.

Vantagens:carregamento atrasado, thread-safe (o carregamento de classe no java é mutável), e também reduz o consumo de memória

5、Singleton Enumerado

Foram explicadas algumas maneiras de implementar o padrão singleton, mas essas maneiras de implementação não são tão complicadas nem apresentam problemas em algumas situações.

Código de exemplo:

/**
 * Singleton Enumerado
 */
public enum Singleton {
  /**
   * 1.do Java1.5começa a suportar;
   * 2.fornecer gratuitamente o mecanismo de serialização;
   * 3.absolutamente evitar a instância múltipla, mesmo em face de ataques de serialização complexa ou reflexão;
   */
  instance;
  private String others;
  Singleton() {
  }
  public String getOthers() {
    return others;
  }
  public void setOthers(String others) {
    this.others = others;
  }
}

A simplicidade da escrita é o maior benefício do singleton enumerado, o enum no Java é idêntico a uma classe comum, não apenas pode ter campos, mas também pode ter seus próprios métodos. O mais importante é que a criação de instâncias de instância enum é thread-safe e sempre é um singleton em qualquer situação.

Porque é isso? Nas várias implementações do padrão singleton mencionadas acima, há uma situação em que eles podem criar novos objetos novamente, isso é a deserialização.

A serialização permite escrever uma instância de objeto singleton para o disco e, em seguida, lê-la de volta, permitindo obter uma instância de forma eficaz. Mesmo que o construtor seja privado, ao desserializar ainda é possível criar uma nova instância da classe através de meios especiais, o que é equivalente a chamar o construtor da classe. A operação de desserialização fornece um hook especial, onde a classe possui um método privado, instanciado, chamado readResolve(), que permite que os desenvolvedores controlem a deserialização do objeto. Por exemplo, para evitar que o objeto singleton seja recriado durante a deserialização, é necessário adicionar o seguinte método:

private Object readResolve() throws ObjectStreamException {
  return instance;
}

Isso é, retornar o objeto instance no método readResolve, em vez de gerar um novo objeto por padrão. Para enumeração, não há esse problema, porque, mesmo após a desserialização, ela não gera uma nova instância.

Vantagens: Fornece mecanismo de serialização sem custo, previne a instância múltipla de forma absoluta, mesmo que seja confrontado com ataques de serialização complexa ou reflexão.

Desvantagens: A partir do Java1.5Agora começou a suportar.

O que foi abordado acima é o padrão Singleton.5Métodos de criação, que você pode usar conforme suas próprias necessidades reais de projeto, com base em suas vantagens e desvantagens.

Leitores interessados em mais conteúdo sobre Android podem consultar as seções especiais deste site: 'Tutorial de Entrada e Avançado de Desenvolvimento Android', 'Técnicas de Debug e Solução de Problemas Comuns no Android', 'Resumo de Uso dos Componentes Básicos do Android', 'Resumo de Técnicas de View do Android', 'Resumo de Técnicas de Layout do Android' e 'Resumo de Uso dos Controles do Android'.

Espero que o que é mencionado neste artigo ajude a todos na programação de aplicativos Android.

Declaração: O conteúdo deste artigo é extraído da internet, pertence ao respectivo proprietário. O conteúdo é contribuído e carregado voluntariamente pelos usuários da internet. Este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidades legais relacionadas. Se você encontrar conteúdo suspeito de violação de direitos autorais, seja bem-vindo a enviar e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas relevantes. Apenas após verificação, o site deletará o conteúdo suspeito de violação de direitos autorais.)

Você também pode gostar