概述
单例的作用,全局只能有一个实例,程序多次使用的都是同一实例,避免频繁创建对象使内存飙升
懒汉式
/**
* 懒汉式单例,用的时候才实例化,节省资源
* @author xxx
* @since 2023/6/6 17:45
*/
public class LazyManSingleton {
private static LazyManSingleton singleton;
private LazyManSingleton(){}
public static LazyManSingleton getInstance(){
if(singleton == null){
singleton = new LazyManSingleton();
}
return singleton;
}
}
饿汉式
/**
* 饿汉式,类加载的时候就创建实例,避免现场同步问题
* @author xxx
* @since 2023/6/6 17:50
*/
public class HungryManSingleton {
private static final HungryManSingleton singleton = new HungryManSingleton();
private HungryManSingleton(){}
public static HungryManSingleton getInstance(){
return singleton;
}
}
懒汉式+双重校验锁+防指令重排
/**
* 懒汉式+双重校验锁+指令重排
* @author xxx
* @since 2023/6/6 17:54
*/
public class Singleton {
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
volatile防止指令重排:
创建一个对象,在JVM中会经过三步:
(1)为singleton分配内存空间
(2)初始化singleton对象
(3)将singleton指向分配好的内存空间
指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。文字较为晦涩,可以看流程图:
使用volatile关键字可以防止指令重排序,其原理较为复杂,这篇博客不打算展开,可以这样理解:使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换,这样在多线程环境下就不会发生NPE异常了。
转载自原文链接:https://blog.csdn.net/weixin_41949328/article/details/107296517
枚举-单例的终极解决方案
单例模式终极解决方案,防反射,防反序列化
/**
* @author xxx
* @since 2023/6/6 17:39
*/
@Getter
public enum SingletonEnum {
/**
* 实例
*/
INSTANCE
}
使用
public class Context {
public static void main(String[] args) {
// 懒汉式
LazyManSingleton a1 = LazyManSingleton.getInstance();
LazyManSingleton a2 = LazyManSingleton.getInstance();
System.out.println("a1和a2的地址是否相同:" + (a1 == a2));
// 饿汉式
HungryManSingleton b1 = HungryManSingleton.getInstance();
HungryManSingleton b2 = HungryManSingleton.getInstance();
System.out.println("b1和b2的地址是否相同:" + (b1 == b2));
// 双重校验锁+防指令重排
Singleton c1 = Singleton.getInstance();
Singleton c2 = Singleton.getInstance();
System.out.println("c1和c2的地址是否相同:" + (c1 == c2));
// 单例模式终极解决方案,防反射,防反序列化
SingletonEnum obj1 = SingletonEnum.INSTANCE;
SingletonEnum obj2 = SingletonEnum.INSTANCE;
System.out.println("obj1和obj2的地址是否相同:" + (obj1 == obj2));
}
}