793 votos

¿Qué es una manera eficiente de aplicar un patrón singleton en Java?

¿Qué es una manera eficiente de aplicar un patrón singleton en Java?

768voto

Stephen Denne Puntos 17031

El uso de una enumeración:

   public enum Foo {
       INSTANCE;
   }

Joshua Bloch, explicó este enfoque en su Efectiva Java Reloaded hablar en el Google I/O 2008: enlace al video. También ver diapositivas 30-32 de su presentación (effective_java_reloaded.pdf):

La Forma Correcta de Implementar Serializable Singleton

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

Edit: Una parte en línea de "Effective Java" dice:

"Este enfoque es funcionalmente equivalente a la esfera pública, excepto que es más conciso, proporciona la serialización de la maquinaria de forma gratuita, y ofrece un férreo garantía contra la múltiple creación de instancias, incluso en la cara de sofisticados de serialización o los ataques de reflexión. Mientras que este enfoque aún no se ha adoptado ampliamente, con un único elemento de tipo enum es la mejor manera de implementar un singleton."

229voto

Roel Spilker Puntos 2807

Dependiendo del uso, hay varias respuestas "correctas".

Desde java5 la mejor manera de hacerlo es utilizar una enumeración:

public enum Foo {
   INSTANCE;
}

Pre java5, el caso más simple es:

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

Vamos a repasar el código. En primer lugar, desea que la clase tiene que ser la final. En este caso, he usado el final de palabras clave para permitir a los usuarios saber que es definitiva. Entonces usted necesita para hacer el constructor privado para prevenir a los usuarios crear sus propios Foo. Lanzar una excepción desde el constructor impide a los usuarios utilizar la reflexión para crear un segundo Foo. A continuación, crear una private static final Foo campo para celebrar la única instancia, y un public static Foo getInstance() método de la devolución. La especificación de Java se asegura de que el constructor se llama sólo cuando la clase se usa por primera vez.

Cuando usted tiene una muy objetos grandes o pesados de construcción de código Y también tiene otros accesible métodos estáticos o campos que pueden ser usados antes de una instancia es necesario, entonces, y sólo entonces usted necesita para usar la inicialización perezosa.

Puede utilizar un private static class a la carga de la instancia. El código podría ser algo como:

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

Desde la línea private static final Foo INSTANCE = new Foo(); sólo se ejecuta cuando la clase FooLoader se utiliza realmente, este se ocupa de la perezosa la creación de instancias, y se garantiza seguros para subprocesos.

Cuando usted también quiere ser capaz de serializar el objeto que usted necesita para asegurarse de que la deserialización no crean una copia.

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

El método readResolve() se asegurará de que la única instancia que será devuelto, incluso cuando el objeto fue serializado en una anterior a la ejecución del programa.

123voto

Bno Puntos 5688

La solución publicado por Stu Thompson es válido en Java5.0 y posterior. Pero yo preferiría que no lo uso porque creo que es propenso a error.

Es fácil olvidarse de los volátiles de la declaración y de difícil comprender por qué es necesario. Sin la volátil este código podría no ser seguro para subprocesos más debido a la doble control de bloqueo antipattern. Ver más sobre esto en el apartado 16.2.4 de Java Concurrencia en la Práctica. En resumen: Este patrón (antes de Java5.0 o sin el volátiles declaración) podría devolver una referencia a la Barra de objeto que está (todavía) en un estado incorrecto.

Este patrón fue inventado para la optimización del rendimiento. Pero esto no es realmente una preocupación real ya. La siguiente inicialización perezosa código es rápido y-más importante aún - más fácil de leer.

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

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

94voto

Stu Thompson Puntos 16599

Prueba de hilos en 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 atención a las volatile modificador de aquí. :) Es importante porque sin ella, en otros hilos no están garantizados por el JMM (Java Modelo de Memoria) para ver los cambios en su valor. La sincronización de no tener cuidado de que-sólo se serializa el acceso a ese bloque de código.

EDIT 2: @Bno 's respuesta de detalles el enfoque recomendado por el proyecto de Ley Pugh (FindBugs) y es discutible mejor. Leer y votar su respuesta.

91voto

Jonathan Puntos 3229

Olvídate de inicialización diferida, es muy problemático. Esta es la solución más sencilla:

public class A {    

    private static A singleton = new A();

    private A() {}

    public static A getInstance() {
        return singleton;
    }
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X