it-swarm-pt.tech

O que é uma maneira eficiente de implementar um padrão singleton em Java?

O que é uma maneira eficiente de implementar um padrão singleton em Java?

752
Riyaz Mohammed Ibrahim

Use um enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch explicou essa abordagem em seu Effective Java Reloaded talk no Google I/O 2008: link para video . Veja também os slides 30-32 de sua apresentação ( effective_Java_reloaded.pdf ):

O caminho certo para implementar um singleton serializável

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Edit: An porção online de "Java Efetivo" diz: 

"Essa abordagem é funcionalmente equivalente à abordagem de campo público, exceto que é mais concisa, fornece gratuitamente o mecanismo de serialização e fornece uma garantia rígida contra a instanciação múltipla, mesmo diante de sofisticados ataques de serialização ou reflexão. Embora essa abordagem tenha ainda para ser amplamente adotado, um tipo de enum de elemento único é a melhor maneira de implementar um singleton. "

743
Stephen Denne

Dependendo do uso, existem várias respostas "corretas".

Desde o Java5, a melhor maneira de fazer isso é usar um enum:

public enum Foo {
   INSTANCE;
}

Pré Java5, o caso mais simples é:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Vamos repassar o código. Primeiro, você quer que a aula seja final. Nesse caso, usei a palavra-chave final para informar aos usuários que é final. Então você precisa tornar o construtor privado para impedir que os usuários criem seu próprio Foo. Lançar uma exceção do construtor impede que os usuários usem a reflexão para criar um segundo Foo. Em seguida, você cria um campo private static final Foo para manter a única instância e um método public static Foo getInstance() para retorná-lo. A especificação Java garante que o construtor seja chamado apenas quando a classe é usada pela primeira vez.

Quando você tem um objeto muito grande ou um código de construção pesada E também tem outros métodos ou campos estáticos acessíveis que podem ser usados ​​antes que uma instância seja necessária, então, e somente então, você precisa usar a inicialização lenta.

Você pode usar um private static class para carregar a instância. O código ficaria assim:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Como a linha private static final Foo INSTANCE = new Foo(); é executada somente quando a classe FooLoader é realmente usada, isso cuida da instanciação lenta, e é garantida que ela é thread-safe.

Quando você também quiser ser capaz de serializar seu objeto, você precisa ter certeza de que a desserialização não criará uma cópia.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

O método readResolve() irá garantir que a única instância será retornada, mesmo quando o objeto foi serializado em uma execução anterior do programa.

224
Roel Spilker

Disclaimer: Acabei de resumir todas as respostas impressionantes e escrevi em minhas palavras.


Enquanto implementamos o Singleton, temos duas opções
1. Carregamento lento
2. Carregamento antecipado

O carregamento lento adiciona pouca sobrecarga (para ser honesto), use-o somente quando você tiver um objeto muito grande ou um código de construção pesada E também tiver outros métodos ou campos estáticos acessíveis que possam ser usados ​​antes de uma instância ser necessária. você precisa usar inicialização lenta. Caso contrário, escolher o carregamento antecipado é uma boa escolha.

A maneira mais simples de implementar Singleton é 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Tudo é bom, exceto seu singleton carregado cedo. Vamos tentar singleton carregado preguiçoso

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Até aí tudo bem, mas o nosso herói não vai sobreviver enquanto luta sozinho com vários tópicos malignos que querem muitas instâncias do nosso herói. Então, vamos protegê-lo do mal multi threading

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

mas não é o suficiente para proteger o herói, realmente !!! Isso é o melhor que podemos/devemos fazer para ajudar nosso herói 

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Isso é chamado de "Idioma de bloqueio com verificação dupla". É fácil esquecer a declaração volátil e é difícil entender por que isso é necessário.
Para detalhes: http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

Agora temos certeza sobre o fio do mal, mas e sobre a serialização cruel? Temos que ter certeza que, mesmo enquanto não serializamos, nenhum objeto novo é criado

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

O método readResolve() irá garantir que a única instância será retornada, mesmo quando o objeto foi serializado em uma execução anterior do nosso programa.

Finalmente, nós adicionamos proteção suficiente contra threads e serialização, mas nosso código está parecendo volumoso e feio. Vamos dar ao nosso herói um make over

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Sim, este é o nosso mesmo herói :)
Como a linha private static final Foo INSTANCE = new Foo(); só é executada quando a classe FooLoader é realmente usada, isso cuida da instanciação lenta, 

e é garantido que ele é seguro para threads.

E nós chegamos tão longe, aqui está a melhor maneira de conseguir tudo o que fizemos é o melhor caminho possível 

 public enum Foo {
       INSTANCE;
   }

Que internamente será tratado como 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Não é mais medo de serialização, threads e código feio. Também singleton ENUMS são inicializados de forma lenta

Essa abordagem é funcionalmente equivalente à abordagem de campo público, , Exceto que é mais concisa, fornece gratuitamente o mecanismo de serialização E fornece uma garantia rígida contra a instanciação múltipla , Mesmo em face de serialização sofisticada ou ataques de reflexão. Embora essa abordagem ainda não tenha sido amplamente adotada, Um tipo de enum de elemento único é a melhor maneira de implementar um singleton.

-Joshua Bloch em "Java efetivo" 

Agora você deve ter percebido porque o ENUMS é considerado a melhor maneira de implementar o Singleton e obrigado pela sua paciência :)
Atualizado em meu blog

127
xyz

A solução postada por Stu Thompson é válida em Java5.0 e posterior. Mas eu prefiro não usá-lo porque acho que é propenso a erros.

É fácil esquecer a declaração volátil e é difícil entender por que isso é necessário. Sem o volátil, esse código não seria mais seguro para o thread devido ao antipadrão de bloqueio duplo verificado. Veja mais sobre isso no parágrafo 16.2.4 do Java Concurrency in Practice . Resumidamente: Esse padrão (anterior ao Java5.0 ou sem a declaração volátil) poderia retornar uma referência ao objeto Bar que está (ainda) em um estado incorreto.

Esse padrão foi inventado para otimização de desempenho. Mas isso não é mais uma preocupação real. O seguinte código de inicialização lenta é rápido e, mais importante, mais fácil de ler.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
121
Benno Richters

Segmento seguro no Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT: Preste atenção no modificador volatile aqui. :) É importante porque sem ele, outros threads não são garantidos pelo JMM (Java Memory Model) para ver mudanças em seu valor. A sincronização não cuida disso - apenas serializa o acesso a esse bloco de código.

EDIT 2 : @ A resposta de Bno detalha a abordagem recomendada por Bill Pugh (FindBugs) e é discutível melhor. Vá ler e vote em sua resposta também.

94
Stu Thompson

Esqueça a inicialização preguiçosa , é muito problemático. Esta é a solução mais simples:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

Certifique-se de que você realmente precisa. Faça um google para "anti-padrão singleton" para ver alguns argumentos contra ele. Não há nada de errado com isso, suponho, mas é apenas um mecanismo para expor alguns dados/recursos globais, portanto, certifique-se de que esta é a melhor maneira. Em particular, achei a injeção de dependência mais útil, especialmente se você também estiver usando testes de unidade, pois a DI permite que você use recursos ridicularizados para fins de teste.

47
Neil Burroughs

Não se esqueça que o Singleton é apenas um Singleton para o Classloader que o carregou. Se você estiver usando vários carregadores (Containers), cada um pode ter sua própria versão do Singleton.

26
Javamann

Estou perplexo com algumas das respostas que sugerem a DI como uma alternativa ao uso de singletons; estes são conceitos não relacionados. Você pode usar DI para injetar instâncias singleton ou não singleton (por exemplo, thread). Pelo menos isso é verdade se você usar o Spring 2.x, não posso falar por outras estruturas de DI.

Então, minha resposta para o OP seria (em tudo menos o código de amostra mais trivial) para:

  1. Use uma estrutura DI como Spring, então
  2. Faça parte de sua configuração de DI se suas dependências são singletons, escopo de requisição, escopo de sessão ou o que for.

Essa abordagem oferece uma bela arquitetura desacoplada (e, portanto, flexível e testável), em que usar um singleton é um detalhe de implementação facilmente reversível (desde que os singletons que você usa sejam thread-safe, é claro).

21
Andrew Swan

Realmente considere por que você precisa de um singleton antes de escrevê-lo. Há um debate quase religioso sobre o uso deles, que você pode facilmente tropeçar se você google singletons em Java.

Pessoalmente eu tento evitar singletons tão frequentemente quanto possível por muitas razões, mais uma vez a maioria dos quais pode ser encontrada por singletons googling. Eu sinto que muitas vezes os singletons são abusados ​​porque são fáceis de entender por todos, são usados ​​como um mecanismo para obter dados "globais" em um design OO e são usados ​​porque é fácil contornar o objeto gerenciamento do ciclo de vida (ou realmente pensando em como você pode fazer A de dentro de B). Olhe para coisas como Inversão de Controle (IoC) ou Injeção de Dependência (DI) para um meio-campo Agradável.

Se você realmente precisa de um, então a wikipedia tem um bom exemplo de uma implementação adequada de um singleton.

20
Aidos

A seguir, três abordagens diferentes

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) bloqueio verificado dobro/carregamento preguiçoso

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Método de fábrica estática

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

Eu uso o Spring Framework para gerenciar meus singletons. Ele não impõe o "singleton-ness" da classe (o que você não pode fazer mesmo se houver vários carregadores de classes envolvidos), mas fornece uma maneira muito fácil de construir e configurar diferentes fábricas para criar diferentes tipos de objetos.

13
Matt

Versão 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

Carregamento preguiçoso, thread seguro com bloqueio, baixo desempenho por causa de synchronized.

Versão 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Carregamento preguiçoso, thread seguro com alto desempenho non-blocking.

11
coderz

A Wikipédia possui alguns exemplos de singletons, também em Java. A implementação do Java 5 parece bastante completa e é segura para thread (bloqueio duplo verificado).

10
macbirdie

Se você não precisa de carregamento lento, tente

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Se você quiser carregar com preguiça e quiser que seu Singleton seja thread-safe, tente o padrão de checagem dupla 

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Como o padrão de verificação dupla não é garantido para funcionar (devido a algum problema com compiladores, eu não sei nada mais sobre isso.), Você também pode tentar sincronizar todo o método getInstance ou criar um registro para todos os seus Singletons. 

10
Aleksi Yrttiaho

Eu diria que o Enum singleton 

Singleton usando enum em Java geralmente é maneira de declarar enum singleton. O singleton de enumeração pode conter variável de instância e método de instância. Para simplificar, observe também que, se você estiver usando qualquer método de instância, precisará assegurar a segurança do segmento desse método, se afetar o estado do objeto.

O uso de um enum é muito fácil de implementar e não tem inconvenientes em relação a objetos serializáveis, que precisam ser contornados de outras maneiras.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Você pode acessá-lo por Singleton.INSTANCE, muito mais fácil do que chamar o método getInstance() em Singleton.

1.12 Serialização de constantes do Enum

As constantes Enum são serializadas de maneira diferente dos objetos serializáveis ​​ou externalizáveis ​​comuns. A forma serializada de uma constante de enumeração consiste unicamente em seu nome; valores de campo da constante não estão presentes no formulário. Para serializar uma constante de enum, ObjectOutputStream escreve o valor retornado pelo método de nome da constante enum. Para desserializar uma constante de enum, ObjectInputStream lê o nome da constante do fluxo; a constante desserializada é então obtida chamando o método Java.lang.Enum.valueOf, passando o tipo enum da constante junto com o nome da constante recebida como argumentos. Como outros objetos serializáveis ​​ou externalizáveis, as constantes enum podem funcionar como alvos de referências posteriores que aparecem posteriormente no fluxo de serialização.

O processo pelo qual as constantes de enum são serializadas não pode ser personalizado: os métodos writeObject, readObject, readObjectNoData, writeReplace e readResolve específicos da classe definidos pelos tipos enum são ignorados durante a serialização e desserialização. Da mesma forma, todas as declarações de campo serialPersistentFields ou serialVersionUID também são ignoradas - todos os tipos de enum têm uma serialVersionUID fixa de 0L. A documentação de campos e dados serializáveis ​​para tipos enum é desnecessária, pois não há variação no tipo de dados enviados.

Citado do Oracle docs

Outro problema com Singletons convencionais é que uma vez que você implementa a interface Serializable, eles não permanecem mais como Singleton porque o método readObject() sempre retorna uma nova instância como construtor em Java. Isso pode ser evitado usando readResolve() e descartando a instância recém-criada, substituindo por singleton como abaixo 

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Isso pode se tornar ainda mais complexo se o seu Singleton Class manter o estado, já que você precisa torná-los transitórios, mas com o Enum Singleton, a Serialização é garantida pela JVM.


Boa leitura

  1. Padrão Singleton
  2. Enums, Singletons e Desserialização
  3. bloqueio duplo-verificado e o padrão Singleton
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

Pode ser um pouco tarde para o jogo sobre isso, mas há muitas nuances em torno da implementação de um singleton. O padrão de suporte não pode ser usado em muitas situações. E IMO ao usar um volátil - você também deve usar uma variável local. Vamos começar do começo e repetir o problema. Você verá o que quero dizer.


A primeira tentativa pode parecer algo assim:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Aqui temos a classe MySingleton que tem um membro estático privado chamado INSTANCE e um método estático público chamado getInstance (). Na primeira vez que getInstance () é chamado, o membro INSTANCE é nulo. O fluxo cairá então na condição de criação e criará uma nova instância da classe MySingleton. Chamadas subseqüentes para getInstance () irão descobrir que a variável INSTANCE já está definida e, portanto, não criar outra instância MySingleton. Isso garante que haja apenas uma instância do MySingleton que é compartilhada entre todos os chamadores de getInstance ().

Mas esta implementação tem um problema. Aplicativos multithread terão uma condição de corrida na criação da instância única. Se vários encadeamentos de execução atingirem o método getInstance () no (ou próximo) ao mesmo tempo, cada um deles verá o membro INSTANCE como nulo. Isso resultará em cada encadeamento criando uma nova instância MySingleton e, em seguida, configurando o membro INSTANCE.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Aqui, usamos a palavra-chave sincronizada na assinatura do método para sincronizar o método getInstance (). Isso certamente consertará nossa condição de corrida. Threads agora irá bloquear e inserir o método um de cada vez. Mas também cria um problema de desempenho. Essa implementação não apenas sincroniza a criação da instância única, como sincroniza todas as chamadas para getInstance (), incluindo as leituras. As leituras não precisam ser sincronizadas, pois simplesmente retornam o valor de INSTANCE. Como as leituras compõem a maior parte de nossas chamadas (lembre-se, a instanciação só acontece na primeira chamada), teremos um desempenho desnecessário afetado pela sincronização do método inteiro.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Aqui, movemos a sincronização da assinatura do método para um bloco sincronizado que envolve a criação da instância MySingleton. Mas isso resolve o nosso problema? Bem, não estamos mais bloqueando as leituras, mas também demos um passo para trás. Vários threads atingirão o método getInstance () por volta do mesmo horário e todos verão o membro INSTANCE como nulo. Eles então acertarão o bloco sincronizado, onde um obterá o bloqueio e criará a instância. Quando esse encadeamento sair do bloco, os outros encadeamentos enfrentarão o bloqueio e, um por um, cada encadeamento passará pelo bloco e criará uma nova instância de nossa classe. Então, estamos de volta onde começamos.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Aqui nós emitimos outro cheque de dentro do bloco. Se o membro INSTANCE já tiver sido definido, vamos pular a inicialização. Isso é chamado de bloqueio com verificação dupla.

Isso resolve nosso problema de instanciação múltipla. Mais uma vez, nossa solução apresentou outro desafio. Outros encadeamentos podem não "ver" que o membro INSTANCE foi atualizado. Isso é devido ao modo como o Java otimiza as operações de memória. Os segmentos copiam os valores originais das variáveis ​​da memória principal para o cache da CPU. As alterações nos valores são então gravadas e lidas a partir desse cache. Este é um recurso do Java projetado para otimizar o desempenho. Mas isso cria um problema para nossa implementação de singleton. Um segundo thread - sendo processado por uma CPU ou core diferente, usando um cache diferente - não verá as alterações feitas pelo primeiro. Isso fará com que o segundo thread veja o membro INSTANCE como null forçando uma nova instância de nosso singleton a ser criado.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Resolvemos isso usando a palavra-chave volátil na declaração do membro INSTANCE. Isso fará com que o compilador sempre leia e grave na memória principal e não no cache da CPU.

Mas esta simples mudança tem um custo. Como estamos ignorando o cache da CPU, teremos um desempenho cada vez que operarmos com o membro volátil do INSTANCE - o que fazemos 4 vezes. Verificamos a existência (1 e 2), definimos o valor (3) e depois retornamos o valor (4). Pode-se argumentar que esse caminho é o caso da franja, pois apenas criamos a instância durante a primeira chamada do método. Talvez um impacto na criação seja tolerável. Mas até o nosso caso de uso principal, lê, operará no membro volátil duas vezes. Uma vez para verificar a existência, e novamente para retornar seu valor.


private static volatile MySingleton INSTANCE; public static MySingleton getInstance() { MySingleton result = INSTANCE; if (result == null) { synchronized(MySingleton.class) { result = INSTANCE; if (result == null) { INSTANCE = result = createInstance(); } } } return result; }

Eu escrevi um artigo sobre isso recentemente. Desconstruindo o Singleton . Você pode encontrar mais informações sobre esses exemplos e um exemplo do padrão "titular". Há também um exemplo do mundo real mostrando a abordagem volátil de dupla verificação. Espero que isto ajude.

.

6
Michael Andrews

Isto é como implementar uma simples singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Esta é a maneira correta de criar sua singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

Você precisa verificar novamente idiom se você precisar carregar a variável de instância de uma classe preguiçosamente. Se você precisar carregar uma variável estática ou um singleton preguiçosamente, será necessário initilization on demand holder idiom. 

Além disso, se o singleton precisar ser seriável, todos os outros campos precisarão ser temporários e o método readResolve () precisará ser implementado para manter a invariante do objeto singleton. Caso contrário, cada vez que o objeto é desserializado, uma nova instância do objeto será criada. O que readResolve () faz é substituir o novo objeto lido por readObject (), o que forçou o novo objeto a ser coletado como lixo, pois não há nenhuma variável referindo-se a ele.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

Várias maneiras de fazer objeto singleton:

  1. De acordo com Joshua Bloch - Enum seria o melhor.

  2. você pode usar o bloqueio de verificação dupla também.

  3. Mesmo a classe estática interna pode ser usada.

3
Shailendra Singh

Enum singleton

A maneira mais simples de implementar um singleton que seja thread-safe é usando um Enum

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Este código funciona desde a introdução do Enum no Java 1.5

bloqueio duplo verificado

Se você deseja codificar um singleton “clássico” que funciona em um ambiente multithread (a partir do Java 1.5), você deve usar este.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

Isso não é thread-safe antes de 1.5 porque a implementação da palavra-chave volátil foi diferente.

Early loading Singleton (funciona mesmo antes do Java 1.5)

Essa implementação instancia o singleton quando a classe é carregada e fornece segurança de encadeamento.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

Para o JSE 5.0 e superior, use a abordagem Enum, use a abordagem de suporte a singleton estático (uma abordagem de carregamento lento descrita por Bill Pugh) A solução mais recente também é thread-safe sem exigir construções de linguagem especiais (voláteis ou sincronizadas).

2
raoadnan

Outro argumento frequentemente usado contra Singletons são seus problemas de testabilidade. Singletons não são facilmente escarnecedores para fins de teste. Se isso se tornar um problema, eu gostaria de fazer a seguinte pequena modificação:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

O método setInstance adicionado permite definir uma implementação de maquete da classe singleton durante o teste:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Isso também funciona com as abordagens iniciais de inicialização:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Isso tem a desvantagem de expor essa funcionalidade para o aplicativo normal também. Outros desenvolvedores que trabalham com esse código podem ser tentados a usar o método ´setInstance´ para alterar uma função específica e, assim, alterar todo o comportamento do aplicativo, portanto, esse método deve conter pelo menos um bom aviso em seu javadoc.

Ainda assim, para a possibilidade de teste de simulação (quando necessário), essa exposição de código pode ser um preço aceitável a pagar.

2
user3792852

classe singleton mais simples

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Eu ainda acho que depois do Java 1.5, o enum é a melhor implementação de singleton disponível, uma vez que também garante que, mesmo nos ambientes multi-thread - apenas uma instância é criada.

public enum Singleton{ INSTANCE; }

e você está feito !!!

0
shikjohari

Dê uma olhada neste post.

Exemplos de padrões de design do GoF em bibliotecas principais do Java

Na seção "Singleton" da melhor resposta,

Singleton (reconhecível por métodos de criação retornando a mesma instância (geralmente por si só) toda vez)

  • Java.lang.Runtime # getRuntime ()
  • Java.awt.Desktop # getDesktop ()
  • Java.lang.System # getSecurityManager ()

Você também pode aprender o exemplo de Singleton a partir de classes nativas de Java.

0
phi

O melhor padrão singleton que já vi usa a interface Supplier.

  • É genérico e reutilizável
  • Suporta inicialização lenta
  • Ele só é sincronizado até ser inicializado e, em seguida, o fornecedor de bloqueio é substituído por um fornecedor sem bloqueio.

Ver abaixo:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314