2010-03-26 19 views
6
public static MySingleton getInstance() { 
if (_instance==null) { 
    synchronized (MySingleton.class) { 
     _instance = new MySingleton(); 
    } 
} 
return _instance; 
} 

1.¿Hay algún problema con la implementación anterior del método getInstance? 2. ¿Cuál es la diferencia entre las dos implementaciones?patrón singleton en java. inicialización diferida

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

return _instance; 
} 

He visto una gran cantidad de respuestas en el patrón singleton en stackoverflow pero la pregunta que he publicado es conocer principalmente la diferencia de 'sincronizar' en el método y nivel de bloque en este caso particular.

+0

http://stackoverflow.com/questions/70689/efficient-way-to-implement-singleton-pattern-in-java – skaffman

+0

Hay un problema con un intrincado variación de su primer código (una variación que verifica la nulidad, primero fuera del sincronizado y luego dentro de la sincronización). Tiene que ver con la forma en que el compilador y la JVM hacen las cosas. Si está MUY interesado, puede buscar "problema Java de inicialización comprobado doble". – helios

+0

Puede que le interese este famoso artículo sobre "bloqueo comprobado doble", que básicamente llega a la conclusión de que está roto: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf –

Respuesta

27

1.¿Hay algún problema con la implementación anterior del método getInstance ?

No funciona. Puede terminar con varias instancias de su Singleton.

2.¿Cuál es la diferencia entre las dos implementaciones?

El segundo funciona, pero requiere sincronización, lo que podría ralentizar el sistema cuando tiene muchos accesos al método desde diferentes subprocesos.

La correcta implementación más sencilla:

public class MySingleton{ 
    private static final MySingleton _instance = new MySingleton(); 
    private MySingleton(){} 
    public static MySingleton getInstance() { 
     return _instance; 
    } 
} 

más corto y mejor (con seguridad serializable):

public enum MySingleton{ 
    INSTANCE; 

    // methods go here 
} 

inicialización perezosa de hijos únicos es un tema que llama la atención manera fuera de proporción con su la utilidad práctica real (la OMI argumentando sobre las complejidades del bloqueo con doble verificación, para lo cual su ejemplo es el primer paso, no es más que un concurso de meadas).

En el 99% de los casos, no necesita ninguna inicialización diferida, o el "init cuando la clase se refiere por primera vez" de Java es lo suficientemente bueno. En el 1% restante de los casos, esta es la mejor solución:

public enum MySingleton{ 
    private MySingleton(){} 
    private static class Holder { 
     static final MySingleton instance = new MySingleton(); 
    } 
    static MySingleton getInstance() { return Holder.instance; } 
} 
+1

las enumeraciones no se inicializan con pereza, aunque – skaffman

+0

Puntos buenos Michael. Me gusta su implementación directa. – Adrian

+0

@Tadeusz: gracias, corrigió –

5

1.¿Hay algún problema con la implementación anterior del método getInstance ?

Sí, la palabra clave sincronizada también debe incluir la instrucción if. Si no es así, dos o más hilos podrían llegar al código de creación.

2.¿Cuál es la diferencia entre las dos implementaciones?

La segunda implementación es correcta y, desde mi punto de vista, más fácil de entender.

El uso de sincronizado en el nivel de método para un método estático se sincroniza en la clase, es decir, lo que ha hecho en la muestra 1. El uso sincronizado en el nivel de método para un método de instancia se sincroniza en la instancia del objeto.

+0

@OP: en esta área, encontrará algo llamado "patrón de bloqueo con doble verificación". Ahí es donde se verifica, se encuentra el nulo, luego se sincroniza, luego se vuelve a verificar (en caso de que las cosas hayan cambiado mientras tanto), luego se crea. Funciona en muchos idiomas, pero * no funciona en Java * a menos que utilice un campo 'volátil 'para la instancia, que es' muy superior. Lo mejor es con su segunda implementación, especialmente con las JVM recientes que manejan el ingreso de bloques sincronizados de manera muy eficiente. Más información aquí: http://www.ibm.com/developerworks/library/j-jtp02244.html –

+0

@OP re mi comentario anterior, debería haber sido más claro: funciona en muchos * entornos *, pero no en JVM (a menos que use un campo 'volátil' o su equivalente, si corresponde, en el idioma que está usando). Aclarando porque en estos días, Java es solo uno de los muchos idiomas que se compila en bytecode de Java y se ejecuta en la JVM (y de forma similar, aunque más raramente, hay algunos compiladores de Java que compilan código máquina y no usan una JVM) –

2

El segundo es hilo de seguridad, pero tiene la sobrecarga de sincronizada en cada llamada, no importa si la instancia se construye o no.La primera opción tiene un error importante que no tiene una comprobación para if (_instance == null) en el bloque sincronizado para evitar la creación de dos instancias.

4

La primera es defectuosa de dos maneras. Como otros han mencionado aquí, varios subprocesos pueden obtener a través de

if (_instance==null) { 

que esperarían unos a otros, hasta que el objeto está completamente construido, pero que harían la creación de instancias y sustituir la referencia en la variable.

El segundo defecto es un poco más complicado. Un hilo podría entrar en el constructor new MySingleton() y luego la JVM cambia a otro hilo. Otro hilo puede verificar la variable para null, pero puede contener una referencia a un objeto parcialmente construido. Entonces, el otro hilo funciona en Singleton parcialmente construido, eso tampoco es bueno. Por lo tanto, debe evitarse la primera variante.

La segunda variante debería funcionar bien. No le importa demasiado la eficiencia, hasta que identifique esto claramente como bloqueador. Las JVM modernas pueden optimizar las sincronizaciones innecesarias, por lo que en el código de producción real esta construcción nunca puede perjudicar el rendimiento.

4

Los diversos enfoques para singletons perezoso de carga son discutidos por Bob Lee en Lazy Loading Singletons y el enfoque de "derecho" es la Initialization on Demand Holder (IODH) idiom que requiere muy poco código y tiene gastos generales de sincronización cero.

static class SingletonHolder { 
    static Singleton instance = new Singleton();  
} 

public static Singleton getInstance() { 
    return SingletonHolder.instance; 
} 

Bob Lee también explica cuándo quiere cargar un singleton (durante las pruebas y el desarrollo). Honestamente, no estoy convencido de que haya un gran beneficio.

0

que sugeriría la siguiente implementación

public class MySingleTon 
{ 

    private static MySingleton obj; 

    //private Constructor 
    private MySingleTon() 
    { 
    } 


    public static MySingleTon getInstance() 
    { 
    if(obj==null) 
    { 
     synchronized(MySingleTon.class) 
     { 
     if(obj == null) 
     { 
      obj = new MySingleTon(); 
     } 
     } 
    } 
    return obj;  
    } 
} 
+0

Esto todavía está bloqueado y, por lo tanto, sigue siendo incorrecto. –

Cuestiones relacionadas