tutorial sirve que patrones patron para mitocode hacer ejemplos ejemplo diseño como java thread-safety singleton instantiation lazy-evaluation

java - sirve - patrones de diseño mitocode



Patrón para la creación de instancia de singleton perezoso segura para subprocesos en java (6)

La instantánea Singleton, segura para subprocesos, no es fácil de entender para todos los codificadores.

No, en realidad es muy, muy fácil:

public class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ ... } public static Singleton getInstance(){ return instance; } }

Mejor aún, haz que sea una enumeración:

public enum Singleton{ INSTANCE; private Singleton(){ ... } }

Es seguro para subprocesos y es perezoso (la inicialización ocurre en el momento de carga de la clase, y Java no carga las clases hasta que son referidas por primera vez).

El hecho es que el 99% del tiempo no necesita carga perezosa en absoluto . Y del 1% restante, en el 0.9%, lo anterior es lo suficientemente perezoso.

¿Ha ejecutado un generador de perfiles y ha determinado que su aplicación se ajusta al 0,01% que realmente necesita la carga lenta en el primer acceso? No lo creía. Entonces, ¿por qué está perdiendo el tiempo inventando estas abominaciones del código Rube Goldbergesque para resolver un problema que no existe?

La instantánea de Singleton, segura para subprocesos, no es algo fácil de entender para todos los programadores, por lo que quería crear una clase en nuestro marco empresarial que haría el trabajo.

¿Qué piensa usted al respecto? ¿Ves algo malo al respecto? ¿Hay algo parecido en Apache Commons? ¿Cómo puedo hacerlo mejor?

Proveedor.java

public interface Supplier<T> { public T get(); }

LazyThreadSafeInstantiator.java

public class LazyThreadSafeInstantiator<T> implements Supplier<T> { private final Supplier<T> instanceSupplier; private volatile T obj; public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) { this.instanceSupplier = instanceSupplier; } @Override // http://en.wikipedia.org/wiki/Double-checked_locking public T get() { T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won''t hurt. if (result == null) { synchronized(this) { result = obj; if (result == null) { result = instanceSupplier.get(); obj = result; } } } return result; } }

Ejemplo de uso:

public class Singleton1 { private static final Supplier<Singleton1> instanceHolder = new LazyThreadSafeInstantiator<Singleton1>(new Supplier<Singleton1>() { @Override public Singleton1 get() { return new Singleton1(); } }); public Singleton1 instance() { return instanceHolder.get(); } private Singleton1() { System.out.println("Singleton1 instantiated"); } }

Gracias


¿No se ha broken el uso del patrón de bloqueo y el uso de volatile en compiladores JIT y sistemas multi-core / procesador debido al modelo de memoria Java y la posibilidad de ejecución fuera de orden?

Más generalmente, parece que un marco para singletons es una exageración de lo que es esencialmente un patrón bastante sencillo de implementar correctamente.


Estaría de acuerdo con otros carteles y diría que esto parece una exageración, pero he dicho que sí creo que esto es algo que un desarrollador junior puede equivocarse. Creo que debido a que el comportamiento del proveedor que construye el singleton (que se muestra a continuación) va a ser el mismo en casi todos los casos, estaría tentado de poner esto como comportamiento predeterminado en el LazyThreadSafeInstantiator . El uso de la clase interna anónima cada vez que quiera usar un singleton es realmente complicado.

@Override public Singleton1 get() { return new Singleton1(); }

Esto se podría hacer proporcionando un constructor sobrecargado que lleve la Clase al singleton requerido.

public class LazyThreadSafeInstantiator<T> implements Supplier<T> { private final Supplier<T> instanceSupplier; private Class<T> toConstruct; private volatile T obj; public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) { this.instanceSupplier = instanceSupplier; } public LazyThreadSafeInstantiator(Class<t> toConstruct) { this.toConstruct = toConstruct; } @Override // http://en.wikipedia.org/wiki/Double-checked_locking public T get() { T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won''t hurt. if (result == null) { synchronized(this) { result = obj; if (result == null) { if (instanceSupplier == null) { try { Constructor[] c = toConstruct.getDeclaredConstructors(); c[0].setAccessible(true); result = c[0].newInstance(new Object[] {}); } catch (Exception e) { //handle } result = } else { result = instanceSupplier.get(); } obj = result; } } } return result; } }

Esto entonces sería usado como tal.

private static final Supplier<Singleton1> instanceHolder = new LazyThreadSafeInstantiator<Singleton1>(Singleton1.getClass());

Esta es mi opinión es un poco más limpia. También podría ampliar esto para usar argumentos de constructor.


Las miradas sobre ingeniadas a mí.

Realmente no veo cómo ayuda tener clase de ayudante .

En primer lugar, utiliza un lenguaje de doble bloqueo , y se ha demostrado que está roto una y otra vez.

Segundo, si TIENE QUE usar singleton, ¿por qué no inicializar static final instancia static final ?

public class Singleton1 { private static final Singleton1 instanceHolder = new Singletong1( ); public Singleton1 instance() { return instanceHolder; } private Singleton1() { System.out.println("Singleton1 instantiated"); } }

Este código es seguro para subprocesos y se ha demostrado que funciona.

Verifique la respuesta de Vineet Reynolds para saber cuándo debe inicializar la instancia de Singleton en una primera obtención . En muchos casos, creo que ese enfoque también es una exageración.


Para una versión que es más legible (en mi opinión) que la que se presenta en la pregunta, se puede referir al lenguaje Inicialización en el titular de la demanda , presentado por Bill Pugh. No solo es seguro para subprocesos teniendo en cuenta el modelo de memoria Java 5, el singleton también se inicializa de forma perezosa.


Lazy<X> lazyX= new Lazy<X>(){ protected X create(){ return new X(); }}; X x = lazyX.get();

abstract public class Lazy<T> { abstract protected T create(); static class FinalRef<S> { final S value; FinalRef(S value){ this.value =value; } } FinalRef<T> ref = null; public T get() { FinalRef<T> result = ref; if(result==null) { synchronized(this) { if(ref==null) ref = new FinalRef<T>( create() ); result = ref; } } return result.value; } }

excepto tal vez el primer get () en un hilo, todas las llamadas get () no requieren sincronización o lectura volátil. El objetivo original de doble bloqueo se logra.