Pregunta

Estoy intentando localizar un problema en nuestro sistema y el siguiente código me preocupa. Lo siguiente ocurre en nuestro método doPost () en el servlet primario (los nombres han sido cambiados para proteger al culpable):

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

El 'Single' de singleton se ve así:

private static Single theInstance = new Single();

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

public static Single getInstance() {
    return theInstance;
}

Con la forma en que está configurado para utilizar un inicializador estático en lugar de buscar una instancia nula en el método getInstance (), ¿podría reconstruirse una y otra vez?

PS: estamos ejecutando WebSphere 6 con la aplicación en Java 1.4

¿Fue útil?

Solución

Encontré esto en el sitio de Sun:

  

Varios Singletons cargados simultáneamente por diferentes cargadores de clases

     

Cuando dos cargadores de clases cargan una clase,   en realidad tienes dos copias de la   clase, y cada uno puede tener su propio   Instancia de singleton Es decir   particularmente relevante en servlets   funcionando en ciertos motores servlet   (iPlanet por ejemplo), donde cada   Servlet por defecto usa su propia clase   cargador. Dos servlets diferentes   Accediendo a un singleton conjunto lo haremos, en   De hecho, consigue dos objetos diferentes.

     

Se producen más cargadores de clases múltiples   comúnmente de lo que podría pensar. Cuando   Los navegadores cargan clases desde la red.   Para su uso por applets, utilizan un   cargador de clases separado para cada servidor   dirección. Del mismo modo, Jini y RMI.   Los sistemas pueden usar una clase separada   Cargador para las diferentes bases de código.   desde donde se descargan archivos de clase.   Si tu propio sistema usa clase personalizada   cargadores, todos los mismos problemas pueden   surgir.

     

Si es cargado por diferentes cargadores de clases,   dos clases con el mismo nombre, incluso   el mismo nombre del paquete, son tratados como   distintas - incluso si, de hecho, son   byte por byte de la misma clase. los   diferentes cargadores de clase representan   diferentes espacios de nombres que distinguen   clases (aunque las clases   los nombres son los mismos), por lo que los dos   Las clases de MySingleton son de hecho   distinto. (Ver " cargadores de clases como   Mecanismo de espacio de nombres " en Recursos.)   Dado que dos objetos Singleton pertenecen a   dos clases del mismo nombre, lo hará   Aparece a primera vista que hay   dos objetos singleton de la misma   clase.

Cita .

Además del problema anterior, si firstTime () no está sincronizado, también podría haber problemas de subprocesos allí.

Otros consejos

No, no se construirá una y otra vez. Es estático, por lo que solo se construirá una vez, justo cuando la clase es tocada por primera vez por el Classloader.

Única excepción: si tiene varios cargadores de clases.

(de GeekAndPoke ):

alt text

Como han mencionado otros, el inicializador estático solo se ejecutará una vez por cada cargador de clases.

Una cosa que echaría un vistazo es el método firstTime () . ¿Por qué no se puede manejar el trabajo en doPreparations () dentro del propio singleton?

Suena como un conjunto desagradable de dependencias.

No hay absolutamente ninguna diferencia entre usar un inicializador estático y una inicialización perezosa. De hecho, es mucho más fácil desordenar la inicialización perezosa, que también impone la sincronización. La JVM garantiza que el inicializador estático siempre se ejecute antes de que se acceda a la clase y sucederá una vez y solo una vez.

Dicho JVM no garantiza que su clase se cargará solo una vez. Sin embargo, incluso si se carga más de una vez, su aplicación web solo verá el singleton relevante, ya que se cargará en el cargador de clases de la aplicación web o en su principal. Si tiene varias aplicaciones web implementadas, se llamará a firstTime () una vez para cada aplicación.

Lo más evidente que hay que verificar es que firstTime () se debe sincronizar y que el indicador firstTime se establece antes de salir de ese método.

No, no creará varias copias de 'Único'. (El problema del cargador de clases se visitará más adelante)

La implementación que describió se describe como 'Inicialización impaciente' en el libro 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;
    }
}

Sin embargo, el código no es lo que querías. Su código está intentando realizar una inicialización perezosa después de crear la instancia. Esto requiere que toda la biblioteca del cliente realice 'firstTime () / doPreparation ()' antes de usarla. Vas a confiar en que el cliente haga lo correcto, lo que hace que el código sea muy frágil.

Puedes modificar el código de la siguiente manera para que no haya ningún 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;
    }
}

Lamentablemente, esta es una implementación deficiente de la inicialización lenta y no funcionará en un entorno concurrente (como el contenedor J2EE).

Hay muchos artículos escritos sobre la inicialización de Singleton, específicamente en el modelo de memoria. JSR 133 solucionó muchas deficiencias en la memoria de Java Modelo en Java 1.5 y 1.6.

Con Java 1.5 & amp; 1.6, tiene varias opciones y se mencionan en el libro ' Java efectivo ' por Joshua Bloch.

  1. Inicialización impaciente, como la anterior [Artículo 3 de EJ]
  2. Idioma de la clase del titular de la inicialización perezosa [Artículo 71 de EJ]
  3. Tipo de enumeración [EJ Artículo 3]
  4. Bloqueo controlado doble con campo estático 'volátil' [Artículo 71 de EJ]

Las soluciones 3 y 4 solo funcionarán en Java 1.5 y superior. Así que la mejor solución sería # 2.

Aquí está la implementación psuedo.

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 del constructor, por lo que tiene la garantía de obtener la instancia correctamente inicializada. Además, estás volviendo a la carga de clases diferidas de JVM y no necesitas ninguna sincronización 'getInstance ()'.

Una cosa notó que el campo estático theInstance no es 'final'. El ejemplo en Java Concurrency no tiene 'final' pero EJ tiene. Tal vez James pueda agregar más color a su respuesta en el 'cargador de clases' y el requisito de 'final' para garantizar la corrección,

Habiendo dicho eso, hay un efecto secundario que con el uso de 'static static'. El compilador de Java es muy agresivo cuando ve "final estático" e intenta integrarlo lo más posible. Esto se menciona en una publicación de blog de Jeremy Manson .

Aquí hay un ejemplo simple.

archivo: A.java

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

archivo: B.java

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

Después de compilar A.java y B.java, cambia A.java a la siguiente.

archivo: A.java

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

Usted recompila 'A.java' y vuelve a ejecutar B.class. La salida que obtendrías es

Hello World

En cuanto al problema del cargador de clases, la respuesta es sí, puede tener más de una instancia de Singleton en varios cargadores de clases. Puede encontrar más información en wikipedia . También hay un artículo específico en Websphere .

Lo único que cambiaría con respecto a esa implementación de Singleton (aparte de no usar un Singleton en absoluto) es hacer que el campo de instancia sea final. El campo estático se inicializará una vez, en la carga de clases. Dado que las clases se cargan de forma perezosa, efectivamente se obtiene una instanciación perezosa gratuita.

Por supuesto, si se carga desde cargadores de clases separados, obtienes múltiples "singletons", pero eso es una limitación de cada idioma de singleton en Java.

EDIT : los bits firstTime () y doPreparations () parecen sospechosos. ¿No se pueden mover al constructor de la instancia de singleton?

No: la inicialización estática de la instancia solo se realizará una vez. Dos cosas a considerar:

  • Esto no es seguro para la ejecución de subprocesos (la instancia no está " publicada " en la memoria principal)
  • Su método firstTime probablemente se llame varias veces, a menos que esté debidamente sincronizado

En teoría, se construirá una sola vez. Sin embargo, este patrón se rompe en varios servidores de aplicaciones, donde puede obtener múltiples instancias de clases 'singleton' (ya que no son seguras para subprocesos).

También, el patrón de singleton ha sido muy criticado. Consulte, por ejemplo, Singleton te quiero, pero me estás derrumbando

Esto solo se cargará una vez cuando el cargador de clases cargue la clase. Este ejemplo proporciona una mejor implementación de Singleton, sin embargo, es lo más lento posible y seguro para subprocesos. Además, funciona en todas las versiones conocidas de Java. Esta solución es la más portátil entre los diferentes compiladores Java y máquinas virtuales.


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;
}

}

Se hace referencia a la clase interna no antes (y, por lo tanto, no se carga antes por el cargador de clases) desde el momento en que se llama a getInstance (). Por lo tanto, esta solución es segura para subprocesos sin requerir construcciones de lenguaje especiales (es decir, volátiles y / o sincronizadas).

No es obligatorio que la instancia individual sea definitiva (no es una buena idea, porque evitará que cambies su comportamiento usando otros patrones).

En el código a continuación, puede ver cómo se crea una instancia de una sola vez (la primera vez que llama al constructor)

fecha del paquete;

importar java.util.Date;

clase pública USDateFactory implementa DateFactory {     USDATFactory estático privado usdatefactory = nulo;

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top