Diseño singleton de Java

Exija uno:

    Asegúrese de que solo haya una instancia de la clase en una JVM (uso compartido de múltiples subprocesos)

solución

  1) Asegúrese de que el mundo exterior de la clase no construya directamente el objeto
  2) ¿Ponga la instancia de la clase en el grupo? (Grupo de enteros, grupo de cadenas, ...)

Planificar aterrizaje:

Solución 1: crear objetos de instancia cuando se carga la clase, escenarios de aplicación: objetos pequeños (menor uso de memoria)

---> Estilo chino hambriento (seguridad de subprocesos, alta eficiencia de llamadas, pero sin demora en la carga):

class Singleton01{
	//private byte[]array=new byte[1024*1024];
	//1.构建方法私有化
	private Singleton01() {}
	//2.类的内部构建对象
	private static  Singleton01 instance=new Singleton01();
	//3.对外提供对象访问
	public static Singleton01 getInstance() {
		return instance;
	}
	public static void show() {}
	public void display() {}
}

Pensamiento: si el método show se llama mucho y el método de visualización se llama al final o rara vez se llama, provocará la creación del objeto Singleton01 al principio. Si el objeto es grande, provocará un desperdicio de recursos.

Solución 2: Cuándo y cuándo crear una instancia de la clase , escenario de aplicación: objetos grandes (ocupan más memoria), rara vez se utilizan;

---> Estilo perezoso (seguridad de subprocesos, baja eficiencia de llamadas, pero se puede cargar con retraso):

class Singleton02{
	//private byte[]array=new byte[1024*1024];
	//1.构建方法私有化
	private Singleton02() {}
	//2.类的内部构建对象
	private static  Singleton02 instance;
	//3.对外提供对象访问(会有阻塞)
	public synchronized static Singleton02 getInstance() {
		if(instance==null) {
			System.out.println("create()");
			instance=new Singleton02();
		}
		return instance;
	}
	public static void show() {}
	public void display() {}
}

Pensamiento: en el caso de subprocesos múltiples, se requieren bloqueos. Si el siguiente código no está bloqueado, el objeto se construirá dos veces. El subproceso múltiple con bloqueos bloqueará

	static void doMethod02() {
		Thread t1=new Thread() {
		   @Override
		   public void run() {
			  Singleton02.getInstance();
			  Singleton02.getInstance();
		   }	
		};
		Thread t2=new Thread() {
			@Override
			public void run() {
				Singleton02.getInstance();
				Singleton02.getInstance();
			}	
		};
		t1.start();
		t2.start();
	}

	public static void main(String[] args) {
		doMethod02();
	}

Resultado de salida:

create()
create()

Opción 3: Reducir el bloqueo sobre la base de la Opción 2, la doble verificación reduce una gran cantidad de bloqueo

---> Double CheckLock se da cuenta de un solo caso: DCL es el mecanismo de juicio de doble bloqueo ( debido al modelo subyacente de JVM, pueden ocurrir problemas ocasionales, por lo que no se recomienda ):

¿El papel de la palabra clave volátil?
    1) Asegurar la visibilidad de las variables entre múltiples hilos (un hilo modifica el valor de esta variable y los otros son visibles inmediatamente)

               -------- Artículo de referencia: https://blog.csdn.net/qianzhitu/article/details/103052040

   2) Se prohíbe el reordenamiento de instrucciones (la JVM ha optimizado la ejecución de instrucciones)

class Singleton03{//应用场景:大对象(占用内存比较多),稀少用
	//private byte[]array=new byte[1024*1024];
	//1.构建方法私有化
	private Singleton03() {}
	//2.类的内部构建对象
	private static volatile Singleton03 instance;
	//3.对外提供对象访问(会有阻塞)
	//3.1多个线程并发访问此方法是否会有线程不会被阻塞?(有的)
	//3.2为什么synchronized内部还要有一次判断?(确保对象只创建1次)
	public static Singleton03 getInstance() {
		if(instance==null) {
			synchronized(Singleton03.class) {
				System.out.println("synchronized");
				if(instance==null) {
					//System.out.println("create()");
					instance=new Singleton03();
					//对象创建过程(开辟内存,初始化属性,调用构造方法,为instance赋值)
				}
			}
		}
		return instance;
	}
}

Solución 4: Implemente la carga diferida de objetos basada en clases internas. Sobre la base de la Solución 3, continúe reduciendo la congestión mientras optimiza el uso de recursos. Escenarios de aplicación: objetos grandes (carga diferida), de uso frecuente

---> Modo de implementación de clase interna estática (seguridad de subprocesos, alta eficiencia de llamadas, carga de retraso)

          Nota: Solo se permite escribir variables estáticas en clases internas estáticas y no se permite escribir variables estáticas en clases internas no estáticas (la sintaxis es así)

class Singleton04{
	//private byte[] array=new byte[1024*1024];
	private Singleton04() {}
	//Singleton04加载时不会加载Inner类
	private static class Inner{
		private static final Singleton04 instance=new Singleton04();
	}
	//可以频繁访问(没有阻塞)
	public static Singleton04 getInstance() {
		//基于内部类实现对象的延迟加载
		return Inner.instance;
	}
	//public static void show() {}
	//public void display() {}
}

Pensando: Cuando se llama al método show, no hará que se inicialice la variable de matriz, ni que se cargue la clase Inner; cuando la instancia se obtiene llamando al método getInstance, llamar a Inner.instance hará que se cargue la clase interna estática Inner, inicializará la instancia y luego ejecutará new Singleton04 (), hace que la matriz se inicialice y se da cuenta de la carga diferida de objetos grandes.

Esquema 5: basado en enumeración; escenario de aplicación: objetos pequeños, de uso frecuente

---> Clase de enumeración (seguridad de subprocesos, alta eficiencia de llamadas, no se puede cargar con retraso, puede evitar naturalmente llamadas de reflexión y deserialización)

enum Singleton05{//Singleton05.class
	INSTANCE;//此对象可以延迟加载吗?不可以
	//private byte[] array=new byte[1024*1024];
	public static void show() {}
}

transferir:

	static void doMethod05() {
		Singleton05.INSTANCE.show();
	}

Pensando: El objeto de enumeración construye el objeto cuando se carga la clase y no puede realizar una carga diferida. Si es una matriz de objetos grande, ocupará recursos cuando se cargue la clase, lo cual no es adecuado; por lo que solo es adecuado para objetos pequeños y escenarios de uso frecuente;

Como escoger:

-Los objetos Singleton ocupan menos recursos, no necesitan retrasar la carga, la enumeración es mejor que los tipos hambrientos

-Los objetos Singleton ocupan muchos recursos y deben cargarse con retraso. Las clases internas estáticas son mejores que las perezosas

 

 

Exige dos:

        Asegúrese de que solo haya una instancia de la clase en un hilo (singleton con hilo)

 Reclamación:

        1) Este tipo de instancia tiene solo una copia por hilo (un singleton dentro del hilo)
        2) Este objeto no se puede construir fuera

Aterrizaje de la solución: basado en la implementación de ThreadLocal

package com.java.design;

class Looper{//迭代器(任意的一个对象)
	private Looper() {
		System.out.println("Looper()");
	}
	private static ThreadLocal<Looper> td=new ThreadLocal<>() ;
	public static Looper getLooper() {
		//1.从当前线程获取looper对象
		Looper looper=td.get();//key是谁?
		//2.当前线程没有则创建looper并绑定到当前线程
		if(looper==null) {
			looper=new Looper();
			td.set(looper);//key是谁?ThreadLocal
		}
		//3.返回looper实例
		return looper;
	}
}
public class TestSingleton02 {
	public static void main(String[] args) {
		for(int i=0;i<3;i++) {
			new Thread() {
				@Override
				public void run() {
					Looper.getLooper();
					Looper.getLooper();
					Looper.getLooper();
				}
			}.start();
		}
	}
}

Resultado de salida:

Looper()
Looper()
Looper()

 

  

 

Supongo que te gusta

Origin blog.csdn.net/qianzhitu/article/details/103047262
Recomendado
Clasificación