Implementación perezosa del patrón Title@ Singleton
No digas tonterías y ve directamente al código:
paquete único;
public class LazySingleton { private static LazySingleton instancia=null; privado LazySingleton(){} public static LazySingleton getInstance() { if(instancia==null){ instancia=nuevo LazySingleton(); } instancia de retorno; }
public static void main(String[] args) {
new Thread(()->{System.out.println("线程1:"+LazySingleton.getInstance().hashCode());}).start();
new Thread(()->{System.out.println("线程2:"+LazySingleton.getInstance().hashCode());}).start();
}
}
Resultado:
hilo 1773230249
hilo 2773230249
Si dicha implementación tiene problemas de seguridad de subprocesos, pruébela: el código es el siguiente `public static LazySingleton getInstance() {
if(instance==null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new LazySingleton();
}
return instance;
}`
Resultado:
Subproceso 1: 1564747649
Subproceso 2: 1227684418
Efectivamente, hay múltiples instancias, cómo resolverlo. Agregue un bloqueo de sincronización al método estático.
public static LazySingleton getInstance() { if(instance==null){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (LazySingleton.class) { if(instance==null){ instance=new LazySingleton(); } } } return instance; }
Después de muchas pruebas, los resultados son consistentes, por lo que la realización de un singleton tan perezoso está completa.
Resultado:
Hilo 2: 1798502179
Hilo 1: 1798502179
Pero mirando los resultados, el subproceso 2 se imprime antes que el subproceso 1, y no se imprime en el orden del código, lo que parece ser inconsistente con nuestros resultados esperados Esto se debe a que jvm ha reordenado las instrucciones, cómo romperlo y cargue el código.
private volatile static LazySingleton instance=null;
Al agregar volátiles, las instrucciones no se reordenarán. El resultado será consistente con lo que esperábamos,
el resultado:
Subproceso 1: 219756151
Subproceso 2: 219756151
Entonces, en este momento, se garantizará que el modo singleton sea un singleton como esperas:
todos sabemos cómo crear objetos de varias maneras:
1. Crear un objeto nuevo
2. Crear un objeto a través de la reflexión
3. Crear un objeto a través de la clonación
4. Darse cuenta a través de la deserialización
5,…
package singleton;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazySingleton implements Cloneable,Serializable{
/**
* @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么)
*/
private static final long serialVersionUID = 1L;
private volatile static LazySingleton instance=null;
private LazySingleton(){
}
public static LazySingleton getInstance() {
if(instance==null){
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (LazySingleton.class) {
if(instance==null){
instance=new LazySingleton();
}
}
}
return instance;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException, ClassNotFoundException, CloneNotSupportedException {
// new Thread(()->{System.out.println("线程1:"+LazySingleton.getInstance().hashCode());}).start();
// new Thread(()->{System.out.println("线程2:"+LazySingleton.getInstance().hashCode());}).start();
LazySingleton instance1=LazySingleton.getInstance();
System.out.println("instance1:"+instance1.hashCode());
//反射方式获取对象
Constructor<LazySingleton> declaredconstructor=LazySingleton.class.getDeclaredConstructor();
declaredconstructor.setAccessible(true);
LazySingleton instance2=declaredconstructor.newInstance();
System.out.println("instance2:"+instance2.hashCode());
//通过反序列话获取单例
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(instance1);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
LazySingleton instance3=(LazySingleton)ois.readObject();
System.out.println("instance3:"+instance3.hashCode());
//通过克隆获取对象
LazySingleton instance4=(LazySingleton) instance1.clone();
System.out.println("instance4:"+instance4.hashCode());
}
}
Resultado:
instancia 1: 366712642
instancia 2: 1829164700
instancia 3: 1442407170
instancia 4: 1028566121
Efectivamente, tal diseño singleton todavía tiene la posibilidad de ser atacado.
package singleton;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazySingleton implements Cloneable,Serializable{
/**
* @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么)
*/
private static final long serialVersionUID = 1L;
private volatile static LazySingleton instance=null;
//避免反射产生新的单例
private LazySingleton(){
if(instance!=null){
throw new RuntimeException("单例已经实例化了,不能再实例化对象了");
}
}
public static LazySingleton getInstance() {
if(instance==null){
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (LazySingleton.class) {
if(instance==null){
instance=new LazySingleton();
}
}
}
return instance;
}
//避免克隆产生多个实例。
@Override
public LazySingleton clone(){
return instance;
}
//避免反序列化导致产生多个实例
private Object readResolve(){
return instance;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException, ClassNotFoundException, CloneNotSupportedException {
// new Thread(()->{System.out.println("线程1:"+LazySingleton.getInstance().hashCode());}).start();
// new Thread(()->{System.out.println("线程2:"+LazySingleton.getInstance().hashCode());}).start();
LazySingleton instance1=LazySingleton.getInstance();
System.out.println("instance1:"+instance1.hashCode());
//通过反序列话获取单例
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(instance1);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
LazySingleton instance3=(LazySingleton)ois.readObject();
System.out.println("instance3:"+instance3.hashCode());
//通过克隆获取对象
LazySingleton instance4=(LazySingleton) instance1.clone();
System.out.println("instance4:"+instance4.hashCode());
//反射方式获取对象
Constructor<LazySingleton> declaredconstructor=LazySingleton.class.getDeclaredConstructor();
declaredconstructor.setAccessible(true);
LazySingleton instance2=declaredconstructor.newInstance();
System.out.println("instance2:"+instance2.hashCode());
}
}
Resultado:
instancia1: 366712642
instancia3: 366712642
instancia4: 366712642
Excepción en hilo "main" java.lang.reflect.invocationTargetException
en Sun.reflect.nativeconstructorReSeMeMeMeMeM.NeWinStance0 (Método nativo)
en Sun.ReflectEdRuctUdRuctUctUdRUCTURCO (NATIVECTRUCT (NATIVECLUCTPLUCT. oraccessorImpl.java: 62 )
en sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
en java.lang.reflect.Constructor.newInstance(Constructor.java:408)
en singleton.LazySingleton.main(LazySingleton.java:74)
Causado por: java .lang.RuntimeException: Ya se ha creado una instancia de singleton y ya no se puede crear una instancia del objeto
en singleton.LazySingleton.(LazySingleton.java:23)
… 5 más
A través de la implementación anterior, se puede evitar que el modo singleton sea destruido.