padrões de design (1) - Singleton detalhada

I. Introdução

  Antes do Ano Novo em casa, eu comprei um 《Head First设计模式》ponto de vista, depois de ler sempre quis escrever uma série de padrões de design blog, mas não começou. Hoje, só para ver a 《Java并发编程的艺术》implementação deste livro em ambas as formas de padrão de multi-threaded Singleton fez uma introdução detalhada, deixe-me ter uma melhor compreensão dos mecanismos da sua aplicação, portanto, aproveitar esta oportunidade para falar sobre em uma forma de realização de modo único.


Em segundo lugar, o texto

 2.1 O que é um singleton

  padrão de design Singleton é relativamente simples, mas é um amplamente utilizado. padrão Singleton é definido como se segue:

Certifique-se que uma classe tem apenas um objeto, e o objeto de fornecer interface de acesso global .

  Isso deve ser bem entendido, usando o padrão Singleton para definir uma classe, só é capaz de criar um objeto por ela, e fornecer uma interface, de modo que o global pode obter este objeto. Note-se que apenas um objeto desta classe não é um contrato, mas por alguns meios, forçar esta classe pode ter apenas um objeto. O padrão Singleton existem cinco formas clássicas:

  1. -Estilo chinês com fome
  2. preguiçoso estilo
  3. fechaduras de duplo controlo
  4. classe interna estática
  5. implementação de classe Enum

  Aqui vou apresentar um por um os cinco implementação.


 2.2 fórmula fome

  estilo homem faminto deve ser um singleton em implementação o mais simples, nós olhar directo sobre o código:

public class Singleton {
    // 单例对象
    public static Singleton singleton = new Singleton();

    // 构造器被私有
    private Singleton() {
    }
}

  Singleton alcançar o acima deve ser bem entendido, em sala de aula, nós inicializar um direto staticobjeto, então o construtor está definido private, então você não pode criar objetos dessa classe fora da classe, por isso a classe terá um objeto único singleton. Esta é também origem fome do nome de Han - não espere, imediatamente criado, como um louco com fome (solução envergonhado). E este é um objeto singleton publictipo, que também garante que ele pode ser acessado globalmente.

  Esta implementação é muito simples, e em um ambiente multithreaded é seguro, porque o membro da classe estática é inicializado quando a classe é carregada, e o processo de carregamento de classe tem sido pela JVMimplementação síncrona do fio, que não é uma classe vai ser carregado duas vezes. No entanto, essa implementação também tem a desvantagem de que o objeto singleton é uma classe quando carregado para criar, assumir que precisamos criar é um objeto singleton muito complexo, e que ele vai pegar um monte de recursos, muito lento para baixo a classe de eficiência de carregamento . Para resolver este problema, para que haja um homem preguiçoso.

 2,3 estilo preguiçoso

  Preguiçoso tipo de aplicação é apenas quando um objecto Singleton é usado pela primeira vez, foi criado. A origem deste é o nome do homem preguiçoso - única lembrança que se preocupar quando ela se moverá (solução novamente envergonhado). Aqui está um tipo preguiçoso olhar de código.

(1) implementar processos seguros

  fio único, o preguiçoso alcançar a seguinte fórmula:

public class Singleton {
    // 单例对象为private
    private static Singleton singleton;

    // 构造器被私有
    private Singleton() {
    }

    // 获取单例对象的方法
    public static Singleton getSingleton() {
        // 若单例对象还没创建,则先创建它
        if (null == singleton)
            singleton = new Singleton();
        return singleton;
    }
}

  O código acima também deve ser bem compreendida, desta vez, vamos Singleton referências de objeto são definidas como privadas, mas um publico getmétodo para obter o objeto Singleton, que é globalmente de interface acessível. Neste getmétodo, primeiro determinar se o objeto é criado, se não for criado, você deve criá-lo, o que significa que esse objeto vai primeiro getser criado, terá acesso, em seguida, direto. Ao mesmo tempo, porque o construtor desta classe é private, por isso, em circunstâncias normais, esta classe não pode criar outros objetos.

(2) para conseguir a segurança do thread

  Acima desta implementação, uma boa solução para o problema da fome estilo abrandar a velocidade de carregamento. Mas tem uma falha que não pode ser usado no caso de rosca multi-. No getSingletonexistem maneiras em três linhas de código, nós consideramos como multi-thread: Se existem dois threads sejam executados simultaneamente com a primeira frase do código, para determinar se o objeto está vazia, então o objeto não foi criado, assim que dois tópicos serão o segundo período do código em execução em uma tentativa de criar um objeto. O primeiro segmento para criar um objeto, atribuído ao singletonentão segundo segmento também cria um novo objeto, atribuir novamente singleton, ele estará na frente das referências a tampa. Criar vários objetos, que violou o princípio da singletons, a fim de resolver esta situação, precisamos de sincronização multi-thread com este método, isto é, o código a seguir:

public class Singleton {
    private static Singleton singleton;
    private Singleton() {
    }

    // 使用synchronized关键字同步方法
    public synchronized static Singleton getSingleton() {
        if (null == singleton)
            singleton = new Singleton();
        return singleton;
    }
}

  Usamos a synchronizedpalavra-chave método de sincronização, de modo que, cada vez que há apenas um método de execução da linha, a situação acima não ocorrerá novamente. No entanto, novos problemas surgiram, cada thread chama o getSingletonmétodo antigo, você precisa obter um bloqueio, o que irá afectar significativamente a eficiência da operação. E descobrimos que, quando apenas o primeiro é criar objetos precisam ser sincronizados, quando um objeto é criado, essa sincronização não é necessário, ou seja, objetos que são criados após a conclusão, isso não reduz a eficiência da sincronização é totalmente sem sentido. Para resolver este problema, alguém veio com a seguinte desta forma.


 2.4 fechaduras de duplo controlo

  Ainda o mesmo, assim que nós checar o código de bloqueio:

public class Singleton {
    // 需要加上volatile修饰
    public volatile static Singleton singleton;
    
    private Singleton() {
    }

    public static Singleton getSingleton() {
        // 1、先判断singleton是否被创建
        if (null == singleton) {
            // 2、开始创建对象,但是需要先同步
            synchronized (Singleton.class) {
                // 3、在正式创建前,再次判断
                if (null == singleton)
                    singleton = new Singleton();    // 4、创建对象
            }
        }
        return singleton;
    }
}

  Verifique o código de bloqueio e preguiçoso estilo semelhante, mas getSingletona realização de mais complexo, vamos dar uma olhada no que ele tem feito algo parecido com isto:

  1. Como um primeiro passo e estilo preguiçoso, primeiro determinar se o objeto singleton foi criado, se eles são criados, basta devolver o objeto, caso contrário tentar criar um objeto;
  2. Criar código objeto é synchronizedcercada sincronização blocos, a fim de evitar que o processo de criação de um objeto segmento, outro segmento pode também tentar criar um objeto, e usando o objeto de sincronização Singletonclasse de classobjeto;
  3. No synchronizedbloco de código, nós determinar se o objeto está vazio novamente, caso apenas para criar um vazio, ou não criado. Por que precisamos para determinar isso de novo? Por exemplo, um fio Acorrendo para o topo do código de 2posição neste momento foi CPUinterrompido, CPUpor sua vez, executar outro segmento B, Bo segmento a ser executado 1, ele descobriu que o objeto não é criado e começou a executar o seguinte código, e não foi CPUsuspensa, por isso criou com sucesso objeto. Depois CPUcurrículo Acorrendo tópicos, Aretomar a partir de onde parou, ou seja, o código 2em, continua a correr para baixo, se o código não for 3julgado, Ao fio irá criar o objeto novamente, e, a fim de impedir que isto aconteça, é necessário determinar novamente;

  Depois de usar o código acima, podemos ver que somente quando o objeto não é criado, será executado synchronizedo bloco de código, quando o objeto é criado com sucesso, não será mais executado o código a ser sincronizado, que é muito habilmente resolvido o homem preguiçoso problemas de sincronização desnecessário. No entanto, este código pode parecer para resolver o problema, mas não olhamos tão simples, ele existe em um problema muito escondido. Veja abaixo este código:

singleton = new Singleton();

  Ele apenas se parece com um código, mas na verdade ele é dividido em três execução de instrução:

  1. Alocar espaço de memória para o objeto;
  2. Inicializar o objecto;
  3. O objecto de referência atribuídos a Singleton;

  Esta é a maneira normal de executar esta declaração, mas o compilador muitas vezes realizar para a eficiência otimizar, sem alterar o resultado final, modificar a instrução (nota: não alterar o resultado aqui, refere-se a um único fio), enquanto que acima esta afirmação é verdadeira. Às vezes, o compilador irá reordenar a acima de três passos, torna-se 1-> 3> 2, o espaço é alocado em primeiro lugar, em seguida, a atribuição, a última inicialização. Se o fizer, haverá alguma melhoria na eficiência, não será afetado em um ambiente single-threaded. No entanto, o problema em um ambiente multithread sofreu. Quando a instrução é reordenada, assumindo esse segmento Apara criar um objeto, executar instruções 1para alocar espaço, em seguida, devido à reordenação, a execução 3da atribuição de variável, desta vez CPUcomutação tópicos, o segmento Bcomeça a execução e Atemporariamente interrompido. BPasse tentativas de adquirir um objeto Singleton, executar a primeira ifdeclaração e descobriu que singleton != null, porque os fios antes de Aexecutar a instrução 3depois da singleton já apontando para objetos, então tópicos Bdiretamente para o objeto é retornado para começar. Desta vez, o problema surge porque Ao fio é nenhuma declaração é executado 2, o objeto não foi inicializado, então o fio Blido no uso do conteúdo é indefinido. Para resolver este problema, necessidade de adicionar a declaração Singleton palavra-chave volátil, variáveis chave modificados, quando você cria um objeto, JVM instruções não reordenar .


 2,5 classe interna estático

  Aqui, novamente introduzir um outro tipo de otimização alcançado preguiçoso, o atraso é conseguido através de uma classe de inicialização interna:

public class Singleton {

    // 内部类实现单例类
    private static class Instance {
        private static final Singleton singletion = new Singleton();
    }

    // 获取单例对象
    public static Singleton getSingleton() {
        return Instance.singletion;
    }
}

  O código acima, nós Singletoncriar uma classe classe interna, essa classe é modificado para o interior private, que só é Singletonacessível internamente. As necessidades singleton objeto a ser criado, são colocados dentro de uma classe criada, mas uma interface de acesso global fornece getSingletonmétodos para acessar classe singleton objeto interno. uma realização tão grande do que é bom? Esta é uma forma natural, ou pela JVMaplicação de carregamento padrão lento. Classe é geralmente utilizado apenas quando carregado em primeiro lugar, isto é, desde que não usam esta classe interna, não será carregado, naturalmente, não irá criar o objecto Singleton. Quando a primeira chamada, desde que o getSingletonmétodo, o método de execução return Instance.singletion;quando a instrução foi apenas a classe de carga interna, objeto singleton só foi criado. E o mais sofisticado, já dissemos, o processo em si é thread-safe de carregamento de classe por JVMnos ajudar a conseguir a sincronização thread, portanto o problema de vários threads ao mesmo tempo, criar um objeto singleton não aparece.


 implementação da classe 2.6 enumeração

  Deixe-me dizer que o final de execução - implementação de classe de enumeração. Primeiro, olhe para o código:

// enum枚举类
public enum Singleton {

    // 单例对象
    SINGLETON;

    // 单例对象的熟悉
    private String name;

    // 单例对象的方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

  tipo enumerado aprendeu deve saber que o objeto da classe de enumeração natural é um singleton, e enumerou as classes, além de criar objetos de diferentes maneiras, também, você pode ter propriedades e métodos, ou seja, podemos enumerar implementos única classe naturais. Este objeto singleton será carregado no momento da classe de enumeração é criada, que este é um estilo de homem faminto. Criar um único exemplo da eficiência é mais elevada do que o tipo fome normal, uma vez que um único caso da enumeração usando a classe enumeração JVMpara alcançar o subjacente, otimizado. A segunda vantagem do uso de uma grande classe de enumeração é: e a sequência do reflector a fim de evitar danos para a forma de realização de modo único. Aqui para falar sobre.


 padrão Singleton destruir 2,7

  Além de criar objetos usando newpalavras-chave, existem duas outras formas:

  • Criação de objetos através da reflexão;
  • Por serialização e de-serialização criar objetos;

  Olhe para a primeira abordagem, mas vamos construir um exemplo de uma única classe está configurado private, mas de reflexão, este não é um problema porque mesmo reflexão pode acessar membros privados, por isso para a classe singleton, podemos adotar o seguinte código para criar um objeto:

public class Singleton {

    private Singleton() {
    }

    public static void main(String[] args) throws Exception {
        // 反射创建对象
        Singleton singleton = Singleton.class.newInstance();
        System.out.println(singleton);
    }
}

  Desta forma, ele quebrou do padrão Singleton só pode criar um objeto em princípio. Antes quatro maneiras de alcançar padrão Singleton pode ser refletido através da criação de um objeto, mas um tipo de enumeração foi capaz de evitar este problema. No Classobjecto newInstancemétodo, se o tipo do objecto detectado precisa de criar esse enumtipo, que vai accionar uma excepção (para criar Classobjectos serão).

  A segunda maneira consiste em utilizar a sequência de destruição, é simplesmente armazenada no primeiro objecto através do fluxo de arquivo, em seguida, através do objecto corrente lido a partir do arquivo, e, em seguida, ler o objecto original seria dois objectos, o objeto original é uma cópia. Antes quatro maneiras de alcançar padrão Singleton não pode evitar este problema, no entanto, podemos enumerar. tipo enumerado pela JVMimplementação subjacente de uma única forma de realização, mesmo utilizando uma sequência de leitura para fora ou o alvo original, em vez de uma cópia. Então, tipos enum Singleton é uma maneira muito segura e simples.


Em terceiro lugar, o resumo

  Quando queremos alcançar um tipo de homem com fome, isto é, criar um objeto imediatamente quando as cargas de classe, este é o melhor alcançado usando classe de enumeração, simples e eficiente; quando você quer alcançar tipo preguiçoso, é recomendado o uso de uma classe interna estática, simples, sem fio questões de segurança Cheng, e maior eficiência.


Em quarto lugar, a referência

Acho que você gosta

Origin www.cnblogs.com/tuyang1129/p/12549743.html
Recomendado
Clasificación