设计模式-单例模式

概述

单例的作用,全局只能有一个实例,程序多次使用的都是同一实例,避免频繁创建对象使内存飙升

懒汉式

/**
 * 懒汉式单例,用的时候才实例化,节省资源
 * @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));
    }
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值