Pergunta

Eu estou tentando rastrear um problema em nosso sistema e as seguintes preocupações código me. Ocorre o seguinte em nosso método doPost () no servlet primário (os nomes foram alterados para proteger o culpado):

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

O singleton 'single' esta aparência:

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

Com a forma como este está definido para usar um estático initializer em vez de verificar para um theInstance nulo no método getInstance (), poderia este get reconstruída uma e outra vez?

PS - Nós estamos correndo WebSphere 6 com a App em Java 1.4

Foi útil?

Solução

Eu encontrei isso no site da Sun:

Vários Singletons Simultaneamente carregados pelas diferentes carregadores de classe

Quando dois carregadores de classe carregar uma classe, você realmente tem duas cópias do classe, e cada um pode ter sua própria Singleton exemplo. Isso é particularmente relevante no servlets correndo em certos mecanismos de servlet (IPlanet por exemplo), onde cada servlet por padrão utiliza a sua própria classe carregador. Dois servlets diferentes acessando um Singleton conjunta, em fato, obter dois objetos diferentes.

Vários carregadores de classe ocorrem mais comumente do que você imagina. Quando navegadores carregar classes de rede para uso por applets, eles usam um carregador de classe separada para cada servidor endereço. Da mesma forma, Jini e RMI sistemas pode utilizar uma classe separada carregador para as diferentes bases de código a partir do qual o download de arquivos de classe. Se o seu próprio sistema utiliza classe personalizada carregadores, todas as mesmas questões podem surgir.

Se carregado por diferentes carregadores de classe, duas classes com o mesmo nome, mesmo o mesmo nome do pacote, são tratados como distinta - mesmo se, na verdade, eles são byte-por-byte da mesma classe. o diferentes carregadores de classe representam namespaces diferentes que distinguem classes (embora as classes' nomes são os mesmos), de modo que os dois aulas MySingleton são de fato distinta. (Consulte "Classe Carregadeira como Mecanismo Namespace" em Recursos.) Desde dois objetos Singleton pertencem duas classes com o mesmo nome, será parecer à primeira vista que existem Singleton dois objetos da mesma classe.

Citation .

Além do problema acima, se firstTime() não está sincronizado, você poderia ter enfiar problemas lá também.

Outras dicas

Não, não se construiu uma e outra vez. É de estática, por isso só vai ser construída uma vez, certo quando a classe é tocada pela primeira vez pela Classloader.

Apenas exceção -. Se acontecer de você ter vários carregadores de classe

(de GeekAndPoke ):

text alt

Como já foi mencionado, o inicializador estático só será executada uma vez por carregador de classe.

Uma coisa que eu iria dar uma olhada é o método firstTime() - por que não pode o trabalho em doPreparations() ser tratado dentro do próprio Singleton

soa como um conjunto desagradável de dependências.

Não há absolutamente nenhuma diferença entre usar um inicializador estático e inicialização lenta. Na verdade, é muito mais fácil de estragar a inicialização lenta, o que também reforça a de sincronização. As garantias de JVM que a estática initializer é sempre executado antes que a classe é acessado e isso vai acontecer uma vez e apenas uma vez.

Dito isto faz JVM não garante que a sua classe será carregado somente uma vez. No entanto, mesmo se ele é carregado mais de uma vez, sua aplicação web ainda verá apenas o singleton relevante, uma vez que irá ser carregada tanto no carregador de classe de aplicações web ou de sua controladora. Se você tem vários aplicativos web implantado, então firstTime () será chamado uma vez para cada aplicação.

As coisas mais aparentes a verificar é que firstTime () tem de ser sincronizado e que o sinalizador firstTime é definida antes de sair do referido método.

Não, não vai criar várias cópias de 'Single'. (Edição Classloader serão visitados mais tarde)

A implementação você delineou é descrito como 'Inicialização Ansioso' por no livro de Briant Goetz - ' Java Concurrency in Practice '.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {
        return theInstance;
    }
}

No entanto, o código não é que você queria. Seu código está tentando executar lazy-inicialização após a instância é criada. Isto requer toda a biblioteca do cliente para executar 'firstTime () / doPreparation ()' antes de o utilizar. Você está indo para confiar no cliente para fazer coisa certa que tornar o código muito frágil.

Você pode modificar o código como o seguinte para não haverá qualquer código duplicado.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {   
        // check for initialization of theInstance
        if ( theInstance.firstTime() )
           theInstance.doPreparation();

        return theInstance;
    }
}

Infelizmente, esta é uma má aplicação de inicialização lenta e isso não vai funcionar em ambiente concorrente (como recipiente J2EE).

Há muitos artigos escritos sobre a inicialização Singleton, especificamente no modelo de memória. JSR 133 abordou muitas fraqueza na memória Java modelo em Java 1.5 e 1.6.

Com Java 1.5 e 1.6, você tem várias opções e eles são mencionados no livro ' eficaz Java ' por Joshua Bloch.

  1. Eager Initialziation, como o acima [EJ item 3]
  2. preguiçoso initalization Titular Classe Idiom [EJ item 71]
  3. Enum Tipo [EJ item 3]
  4. dobro verificado bloqueio com 'volátil' campo estático [EJ item 71]

Solução 3 e 4 só irá funcionar em Java 1.5 e acima. Portanto, a melhor solução seria # 2.

Aqui é o pseudo-implementação.

public class Single
{
    private static class SingleHolder
    {
        public static Single theInstance = new Single();
    }

    private Single() 
    { 
        // load properties
        doPreparation();
    }

    public static Single getInstance() 
    {
        return SingleHolder.theInstance;
    }
}

Observe que 'doPreparation ()' está dentro do construtor assim que você é garantia para obter a instância devidamente inicializado. Além disso, você está piggying volta no carregamento de classe preguiçoso do JVM e não precisam de qualquer sincronização 'getInstance ()'.

Uma coisa que você notou que campo estático theInstance é não 'final'. O exemplo em Java Concurrency não tem 'final', mas EJ faz. Talvez James pode adicionar mais cor a sua resposta em 'carregador de classe' e exigência de 'final' para correção de garantia,

Dito isto, há um efeito colateral que com o uso de 'static final'. compilador Java é muito agressivo quando se vê 'estáticos finais' e tentativas para inline-lo tanto quanto possível. Isto é mencionado na uma postagem de blog por Jeremy Manson .

Aqui está um exemplo simples.

arquivo: A.java

public class A
{
    final static String word = "Hello World";
}

arquivo: B.java

public class B
{
    public static void main(String[] args) {
        System.out.println(A.word);
    }
}

Depois de compilar tanto A.java e B.java, você muda A.java a seguir.

arquivo: A.java

public class A
{
    final static String word = "Goodbye World";
}

Você recompilar 'A.java' e execute novamente B.class. A saída que se obtém é

Hello World

Quanto à questão carregador de classe, a resposta é sim, você pode ter mais de uma instância de Singleton em vários carregadores de classe. Você pode encontrar mais informações sobre wikipedia . Há também um artigo específico sobre Websphere .

A única coisa que eu mudaria sobre essa implementação Singleton (outro do que não usar um Singleton em tudo) é fazer com que o campo de instância final. O campo estático será inicializado uma vez, na carga horária. Como as classes são carregados preguiçosamente, você começa efetivamente instanciação preguiçoso gratuitamente.

Claro que, se ele é carregado de carregadores de classes separadas você tem vários "singletons", mas isso é uma limitação de cada idioma Singleton em Java.

Editar : O firstTime () e doPreparations () bits fazer olhar embora suspeito. eles não podem ser movidos para o construtor da instância singleton?

Não - a inicialização estática do instance só vai ser feito uma vez. Duas coisas a considerar:

  • Este não é thread-safe (a instância não é "publicado" para a memória principal)
  • O seu método firstTime é provavelmente chamado várias vezes, a menos que devidamente sincronizado

Em teoria, será construída apenas uma vez. No entanto, este padrão breaks em vários servidores de aplicação, onde você pode obter várias instâncias de classes 'únicas' (uma vez que não são thread-safe).

Além disso, o padrão Singleton foi criticado muito. Ver, por exemplo Singleton Eu te amo, mas você está me trazendo para baixo

Este vai ser carregado somente uma vez, quando a classe é carregada pelo carregador de classe. Este exemplo fornece uma implementação melhor Singleton no entanto, é como lazy-carregado quanto possível e thread-safe. Além disso, ele funciona em todas as versões conhecidas de Java. Esta solução é a mais portátil em diferentes compiladores de Java e máquinas virtuais.


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

A classe interna não é referenciado anteriormente (e, portanto, não antes carregado pelo carregador de classes) do que no momento em que getInstance () é chamado. Assim, esta solução é isenta de segmentos sem a necessidade de construções de linguagem especiais (isto é volátil e / ou sincronizada).

Não é obrigatória para a única instância para ser final (que não é uma boa idéia em tudo, na verdade, porque isso vai evitar que você mude seu comportamento usando outros padrões).

No código abaixo, você pode ver como ele é instanciado somente uma vez (primeira vez que você chamar o construtor)

Data de pacote;

import java.util.Date;

classe pública USDateFactory implementos DateFactory { private static USDateFactory usdatefactory = null;

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top