2010-01-21 17 views
10

Solo estaba pensando en las otras formas de escribir clases singleton. Entonces, ¿se considera esta clase como una clase singleton?clase Singleton en java

 public class MyClass{ 
      static Myclass myclass; 

      static { myclass = new MyClass();} 

      private MyClass(){} 

      public static MyClass getInstance() 
      { 
       return myclass; 
      } 
     } 

como el bloque estático se ejecuta solo una vez.

+3

Esto es equivalente a 'static Myclass myclass = new MyClass()' – pjp

+0

mejor pregunta, ¿Cuántas implementaciones rotas/con errores del patrón singleton podemos recopilar en un hilo? – james

Respuesta

12

No, no lo es. No declaró myClassprivate static final, ni getInstance() es static. El código tampoco se compila realmente.

Aquí está la Singleton idioma:

public class MyClass { 
    private static final MyClass myClass = new MyClass(); 

    private MyClass() {} 

    public static MyClass getInstance() { 
     return myClass; 
    } 
} 

Debe ser private, por lo que nadie más puede acceder a él directamente. Debe ser static para que solo haya uno. Debe ser final para que no pueda reasignarse. También necesita instanciarlo directamente durante la declaración para que no tenga que preocuparse (tanto) por el subprocesamiento.

Si la carga es caro y por lo tanto más bien prefieren carga diferida del Singleton, y luego considerar el idioma Singleton holder el que hace la inicialización de la demanda en lugar de durante la carga de clase:

public class MyClass { 
    private MyClass() {} 

    private static class LazyHolder { 
     private static final MyClass myClass = new MyClass(); 
    } 

    public static MyClass getInstance() { 
     return LazyHolder.myClass; 
    } 
} 

Debe sin embargo puestas grandes signos de interrogación si necesita un Singleton o no. A menudo no es necesario. Solo una variable estática, una enumeración, una clase de fábrica y/o inyección de dependencia a menudo es la mejor opción.

+0

lo siento lo voy a cambiar ahora. – GuruKulki

+5

Consulte también la * Inicialización a pedido idioma del titular * http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom – pjp

+0

Gracias, actualizó la respuesta. – BalusC

13

Aquí es una forma más de hacerlo:

public enum Singleton{ 
    INSTANCE("xyz", 123); 

    // Attributes 
    private String str; 
    private int i; 

    // Constructor 
    Singleton(String str, int i){ 
    this.str = str; 
    this.i = i; 
    } 
} 

Según Effective Java de Josh Bloch, esta es la mejor manera de implementar Singleton en Java. A diferencia de las implementaciones que involucran un campo privado de instancia estática, que se puede instanciar de forma múltiple a través del abuso de la reflexión y/o serialización, se garantiza que la enumeración será un singleton.

La principal limitación de los singletons de enum es que siempre se crean instancias en el tiempo de carga de clases y no se pueden crear instancias de forma perezosa. Entonces, si, por ejemplo, desea instanciar un singleton usando argumentos en tiempo de ejecución, tendrá que usar una implementación diferente (preferiblemente usando el bloqueo con doble verificación).

+1

De acuerdo, esta instancia será un singleton en el nivel del cargador de clases. – gpampara

+1

De acuerdo (a menos que desee la inicialización perezosa), pero por el bien de los lectores en el futuro, sería útil explicar * por qué * es la "mejor" manera. Bloch dice que "ofrece una garantía inamovible contra la creación de instancias múltiples, incluso frente a ataques de serialización o reflexión sofisticados". –

+1

@KlitosKyriacou, no dude en modificar mi respuesta. – missingfaktor

1

Usando su ejemplo y el uso de la manera del GoF de implementarlo:

public class MyClass{ 
    private static Myclass instance; 

    private MyClass(){ 
     //Private instantiation 
    } 

    public static synchronized MyClass getInstance() //If you want your method thread safe... 
    { 
     if (instance == null) { 
      instance = new MyClass(); 
     } 

     return instance; 
    } 
} 

Espero que esto ayude:

+0

Eso debería ser "instancia de devolución" – phisch

+0

Gracias ... lo actualicé ... gracias por ver mi error. –

2

Aquí es cómo lo hago. Es más rápido, ya que solo requiere un bloque synchronized cuando se crea la instancia.

public class MyClass 
{ 
    private static MyClass INSTANCE=null; 

    private MyClass() 
    { 
    } 

    public static MyClass getInstance() 
    { 
     if(INSTANCE==null) 
     { 
      synchronized(MyClass.class) 
      { 
       if(INSATCNE==null) INSTANCE=new MyClass(); 
      } 
     } 
     return INSTANCE; 
    } 
} 
+3

bloqueo comprobado doble está roto. – james

+1

para corregir el idioma de bloqueo comprobado en J2SE 5 y superior, configure su variable como volátil p. privada estática volátil MyClass INSTANCE = null; –

+0

muchas gracias! bueno saber ! – Pierre

1

Su clase (código original, antes de la edición):

public class MyClass { 
    Myclass myclass; 

    static { myclass = new MyClass();} 

    private MyClass(){} 

    public MyClass getInstance() 
    { 
     return myclass; 
    } 
} 

no es un verdadero Singleton:

  1. el campo myclass no es privado, se puede leer y cambiado desde fuera (suponiendo que tiene un instance para hacerlo en
  2. el campo myclass no es estático, no se puede acceder en la construcción estática o (error de compilación)
  3. el método getInstance() no es estático, por lo que necesita una instancia llamarlo


El código real:

public class MyClass { 
    static Myclass myclass; 

    static { myclass = new MyClass();} 

    private MyClass(){} 

    public static MyClass getInstance() 
    { 
     return myclass; 
    } 
} 

todavía tiene myclass no ser privada (ni final) ... declararlo definitivo ayudaría a evitar que sea involuntariamente cambiado desde el interior de la clase.

private static final Myclass myclass; 
1

Hay 3 formas de crear un singleton en java.

  1. inicialización ansiosos singleton

    public class Test { 
        private static final Test test = new Test(); 
    
        private Test() { 
        } 
    
        public static Test getTest() { 
         return test; 
        } 
    } 
    
  2. perezoso singleton de inicialización (seguro de rosca)

  3. Bill Pugh Singleton con el patrón Holder (preferiblemente el mejor)

    public class Test { 
        private Test(){} 
    
        private static class TestHolder { 
         private static final Test test = new Test(); 
        } 
    
        public static Test getInstance() { 
         return TestHolder.test; 
        } 
    } 
    
1
public class singletonPattern { 
    private static singletonPattern obj; 

    public static singletonPattern getObject() { 
     return obj = (obj == null) ? new singletonPattern() : obj; 
    } 

    public static void main(String args[]) { 
     singletonPattern sng = singletonPattern.getObject(); 
    } 
} 
+0

es una manera muy fácil de declarar singleton. –

0

podría ser un poco tarde al juego en esto, pero una implementación básica sería algo como esto:

public class MySingleton { 

    private static MySingleton INSTANCE; 

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

     return INSTANCE; 
    } 
    ... 
} 

Aquí tenemos la clase MySingleton que tiene un miembro estático privada llamada INSTANCIA y un método público estático llamado getInstance(). La primera vez que se invoca getInstance(), el miembro INSTANCE es nulo. El flujo caerá entonces en la condición de creación y creará una nueva instancia de la clase MySingleton. Las llamadas posteriores a getInstance() encontrarán que la variable INSTANCE ya está configurada y, por lo tanto, no crearán otra instancia de MySingleton. Esto asegura que solo hay una instancia de MySingleton que se comparte entre todas las personas que llaman de getInstance().

Pero esta implementación tiene un problema. Las aplicaciones de subprocesos múltiples tendrán una condición de carrera en la creación de la instancia única. Si varios hilos de ejecución golpean el método getInstance() en (o alrededor) al mismo tiempo, cada uno verá al miembro INSTANCE como nulo. Esto dará como resultado que cada hilo cree una nueva instancia de MySingleton y posteriormente establezca el miembro de INSTANCE.


private static MySingleton INSTANCE; 

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

    return INSTANCE; 
} 

Aquí hemos utilizado la palabra clave sincronizada en la firma del método para sincronizar el método getInstance(). Esto sin duda arreglará nuestra condición de carrera. Los hilos ahora se bloquearán e ingresarán el método uno a la vez. Pero también crea un problema de rendimiento. Esta implementación no solo sincroniza la creación de la instancia única, sino que sincroniza todas las llamadas a getInstance(), incluidas las lecturas. Las lecturas no necesitan sincronizarse ya que simplemente devuelven el valor de INSTANCE. Dado que las lecturas constituirán la mayor parte de nuestras llamadas (recuerde, la creación de instancias solo ocurre en la primera llamada), incurriremos en un golpe de rendimiento innecesario al sincronizar todo el método.


private static MySingleton INSTANCE; 

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

    return INSTANCE; 
} 

Aquí hemos movido la sincronización desde la firma del método, a un bloque sincronizado que envuelve la creación de la instancia MySingleton. Pero, ¿resuelve esto nuestro problema? Bueno, ya no estamos bloqueando las lecturas, pero también hemos dado un paso atrás. Múltiples hilos golpearán el método getInstance() en o cerca del mismo tiempo y todos verán al miembro INSTANCE como nulo. Luego golpearán el bloque sincronizado donde uno obtendrá el bloqueo y creará la instancia. Cuando ese hilo sale del bloque, los otros hilos contendrán por el bloqueo, y uno a uno cada hilo caerá a través del bloque y creará una nueva instancia de nuestra clase. Así que estamos de vuelta donde comenzamos.


private static MySingleton INSTANCE; 

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

    return INSTANCE; 
} 

Aquí emitir otro cheque desde el interior del bloque. Si el miembro INSTANCE ya se ha configurado, omitiremos la inicialización. Esto se llama bloqueo con doble verificación.

Esto resuelve nuestro problema de creación de instancias múltiples. Pero una vez más, nuestra solución ha presentado otro desafío. Otros hilos podrían no "ver" que el miembro INSTANCE se haya actualizado. Esto se debe a la forma en que Java optimiza las operaciones de memoria. Los hilos copian los valores originales de las variables de la memoria principal en la memoria caché de la CPU. Los cambios en los valores se escriben y se leen desde ese caché. Esta es una característica de Java diseñada para optimizar el rendimiento. Pero esto crea un problema para nuestra implementación singleton. Un segundo subproceso - procesado por una CPU o núcleo diferente, que utiliza una memoria caché diferente - no verá los cambios realizados por el primero. Esto hará que el segundo subproceso vea al miembro INSTANCE como nulo forzando una nueva instancia de nuestro singleton para ser creado.


private static volatile MySingleton INSTANCE; 

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

    return INSTANCE; 
} 

resolvemos esto usando la palabra clave volátiles en la declaración del miembro de instancia. Esto le indicará al compilador que siempre lea y escriba en la memoria principal, y no en la memoria caché de la CPU.

Pero este cambio simple tiene un costo. Debido a que estamos pasando por alto el caché de la CPU, tomaremos un golpe de rendimiento cada vez que operamos en el miembro INSTANCE volátil, lo cual hacemos 4 veces. Comprobamos la existencia (1 y 2), establecemos el valor (3) y luego devolvemos el valor (4). Se podría argumentar que este camino es el caso marginal, ya que solo creamos la instancia durante la primera llamada del método. Quizás un golpe de rendimiento en la creación sea tolerable. Pero incluso nuestro caso de uso principal, dice, operará en el miembro volátil dos veces. Una vez para verificar la existencia, y nuevamente para devolver su 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; 
} 

Dado que el impacto en el rendimiento se debe a que opera directamente sobre el elemento volátil, vamos a establecer una variable local con el valor de la volátil y operan sobre la variable local. Esto disminuirá la cantidad de veces que operamos en el volátil, recuperando parte de nuestro rendimiento perdido. Tenga en cuenta que tenemos que establecer nuestra variable local nuevamente cuando ingresemos al bloque sincronizado. Esto garantiza que esté actualizado con los cambios que ocurrieron mientras esperábamos el bloqueo.

Escribí un artículo sobre esto recientemente. Deconstructing The Singleton. Puede encontrar más información sobre estos ejemplos y un ejemplo del patrón "titular" allí. También hay un ejemplo del mundo real que muestra el enfoque volátil doblemente verificado. Espero que esto ayude.

0

La clase de Singloton es la clase en la que se obtiene el mismo objeto siempre. Cuando desee restringir que una clase cree más de un objeto, necesitamos la clase Singleton.

Por ejemplo:

public class Booking { 
    static Booking b = new Booking(); 
    private Booking() { } 
    static Booking createObject() { return b; } 
} 

Para crear objeto de esta clase podemos utilizar:

Booking b1, b2, b3, b4; 
b1 = Booking.createObject(); 
b2 = Booking.createObject(); 
Booking b1, b2, b3, b4; 
b1 = Booking.createObject(); 
b2 = Booking.createObject(); 

b1 y b2 estamos refiriendo a un mismo objeto.

Cuestiones relacionadas