“¿Cómo crear un singleton, sabes cuántas formas?” El entrevistador esbozó
una sonrisa fantasmal ... ¡ Afortunadamente, me acordé del “Libro” que había leído!
Directorio de artículos
Con respecto a la creación de un singleton en el código, el Sr. Zihan resumió 6 formas comunes.
Chino hambriento
Cree objetos directamente, no hay problemas de seguridad de subprocesos.
Elementos clave:
- Una clase solo puede tener una instancia: el constructor está privatizado;
- Debe crear esta instancia usted mismo: variables estáticas de la clase;
- Debe proporcionar esta instancia a todo el sistema usted mismo: (1) exposición directa, (2) método de obtención de variables estáticas;
Cree una instancia del estilo chino hambriento directamente (conciso e intuitivo)
public class Singleton1 {
// 使用public直接暴露
public static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
// 使用public方法直接暴露
public static Singleton1 getInstance(){
return INSTANCE;
}
}
Enumerado (más conciso)
/**
* 推荐
*/
public enum SingletonEnum {
INSTANCE
}
Bloque de código estático estilo chino hambriento (adecuado para instancias complejas)
/**
* 基于外部配置的单例
*/
public class SingletonStaticBlock {
public static final SingletonStaticBlock INSTANCE;
private String info;
static {
Properties por = new Properties();
try {
por.load(SingletonStaticBlock.class.getClassLoader().getResourceAsStream("single.properties"));
} catch (IOException e) {
e.printStackTrace();
}
INSTANCE = new SingletonStaticBlock(por.getProperty("hello"));
}
private SingletonStaticBlock(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
Agregue archivos bajo recurso single.properties
:
hello = Mr.zihan
A continuación lo probamos ~
@Test
public void testSingleton(){
// Singleton singleton = new Singleton();//无法new
// Singleton1 singleton1 = new Singleton1();//无法new
System.out.println(SingletonEnum.INSTANCE);
System.out.println(Singleton1.INSTANCE);
System.out.println(SingletonStaticBlock.INSTANCE.getInfo());
}
Resultados de la prueba:
INSTANCE
com.dwlijx.pattern.creation.single.Singleton1@eec5a4a
Mr.zihan
Hombre flojo
Retrasa la creación de objetos.
Puntos clave:
- (1) Privatización de constructora
- (2) La variable estática guarda la única instancia;
- (3) Proporcionar un método estático para obtener el objeto de esta instancia.
El hilo no es seguro (apto para un solo hilo)
public class SingletonLazy {
// 需要时再实例化
private static SingletonLazy INSTANCE;
private SingletonLazy() {
}
public static SingletonLazy getINSTANCE(){
if (null == INSTANCE){
INSTANCE = new SingletonLazy();
}
return INSTANCE;
}
}
Desventajas: el hilo no es seguro, adecuado para escenarios de un solo hilo.
Pruébelo: es
posible que necesite algunos intentos más.
/**
* 懒汉模式
* 用两个线程getInstance,比对一下实例的hashCode
*/
@Test
public void securityTest() throws ExecutionException, InterruptedException {
Callable<SingletonLazy> callableTask = new Callable<SingletonLazy>() {
@Override
public SingletonLazy call() throws Exception {
return SingletonLazy.getINSTANCE();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<SingletonLazy> f1 = es.submit(callableTask);
Future<SingletonLazy> f2 = es.submit(callableTask);
System.out.println(f1.get().hashCode());
System.out.println(f2.get().hashCode());
es.shutdown();
}
1316864772
1685232414
Bloqueos de clase para garantizar la seguridad de los subprocesos (aplicable a subprocesos múltiples)
Sobre la base del ejemplo anterior, se agrega un medio para controlar la seguridad de subprocesos múltiples.
public class SingletonLazySafe {
private static SingletonLazySafe instance;
//构造方法私有
private SingletonLazySafe() {
}
public static SingletonLazySafe getInstance(){
// 双重校验锁提升效率
if (null == instance){
synchronized (SingletonLazySafe.class){
if (null == instance){
instance = new SingletonLazySafe();
}
}
}
return instance;
}
}
tener una prueba
1316864772
1316864772
Entonces, la pregunta es, ¿sabe por qué hashCode tiene el mismo valor cada vez que se inicializa? Por ejemplo 1316864772
?
Forma de clase interna estática (adecuada para subprocesos múltiples)
(1) Privatización del constructor
(2) La variable estática de la clase interna guarda la única instancia; se
utiliza 内部类的加载特性
: la clase interna no se inicializa automáticamente con la carga e inicialización de la clase externa, sino que se carga e inicializa por separado.
public class InnerClassLazySingleton {
private InnerClassLazySingleton() {
}
public static class Inner{
private static InnerClassLazySingleton INSTANCE = new InnerClassLazySingleton();
}
public static InnerClassLazySingleton getInstance(){
return Inner.INSTANCE;
}
}
tener una prueba
/**
* 懒汉模式-解决线程安全问题
* 用两个线程getInstance,比对一下实例的hashCode
*/
@Test
public void innerClassSingletonSafeTest() throws ExecutionException, InterruptedException {
Callable<InnerClassLazySingleton> callableTask = new Callable<InnerClassLazySingleton>() {
@Override
public InnerClassLazySingleton call() throws Exception {
return InnerClassLazySingleton.getInstance();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<InnerClassLazySingleton> f1 = es.submit(callableTask);
Future<InnerClassLazySingleton> f2 = es.submit(callableTask);
System.out.println(f1.get().hashCode());
System.out.println(f2.get().hashCode());
es.shutdown();
}
Resultados de la prueba:
1285044316
1285044316
Gracias por tu apreciación. Oficial invitado, por favor deje un mensaje antes de irse ~ O deje sus preguntas y las discutiremos juntos ~