• 【Java】【设计模式】单例模式


    单例模式

    • 通过单例模式的方法创建的类在当前进程中只有一个实例对象。
    • 优点:避免频繁创建销毁对象,减轻GC压力。控制全局实例个数:只有一个账号登录。
    • 构造单例模式方式:饿汉式、懒汉式、静态内部类、枚举。

    单例模式特点

    • 私有静态变量:作为类的唯一实例。static 成员在没有创建对象时初始化。静态存储区保存引用指向堆中的唯一对象。
    • 私有构造方法:外部类无法通过构造方法创建对象:每次创建对象都需要构造方法,private 限定权限为当前类。
    • 公有静态方法:公开获取实例的唯一方法。通过 类.方法 调用。静态方法访问静态实例对象。
    • 没有公开的set方法,外部类无法调用set方法创建该实例。

    饿汉式、懒汉式

    • 创建对象实例的时机不同:饿汉式在类加载时创建对象,懒汉式第一次调用时创建对象。

    饿汉式

    • 提前把对象new出来,获取类对象的时候已经存在。
    • 线程安全,在线程访问单例对象之前就已经创建好了。
    • 如果长时间不使用对象,造成资源浪费。
    public class Singleton {
        private static Singleton instance = new Singleton();
    	
        private Singleton(){}
    	
        public static Singleton getInstance(){
            return instance;
        }
    }
    

    懒汉式

    • 第一次调用getInstance()时创建对象。
    • 推荐写法:双重校验锁,线程安全。
    public class Singleton {
        private volatile static Singleton instance;
    
        private Singleton() {}
    
        public  static Singleton getUniqueInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    • 懒汉式详细分解过程:
    1、无锁写法,非线程安全
    • if()条件下可能会创建两个对象。
    public class Singleton {  
        private static Singleton instance;  
    	
        private Singleton (){}  
      
        public static Singleton getInstance() {  
        	if (instance == null) {  
    			instance = new Singleton();  
        	}  
        	return instance;
        }  
    }
    
    2、方法加锁,效率太低
    • synchronized 对获取对象的静态方法加锁,每次获取实例时先锁起来,再进行判断。缺点:处理速度慢。
    public class Singleton {
        private static Singleton instance = null;
    	
        private Singleton(){}
    	
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    3、双检,对类加锁,指令重排会出错
    • synchronized 对类加锁,调用方法不加锁,创建对象时加锁,防止多线程创建多实例。
    • instance = new Singleton() 分为开辟空间,初始化,赋值引用三个指令,初始化和赋值指令可能会重排。
    • 指令重排会导致先分配空间赋值再初始化实例,多线程会调用到未初始化的实例。
    • 正常指令顺序:new在堆开辟空间,调用构造方法初始化对象,返回空间地址赋值。
    public class Singleton {
        private static Singleton instance = null;
    	
        private Singleton(){}
    	
        public static Singleton getInstance(){
            if (instance == null){
                synchronized (Singleton.class) {
                    if (instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    4、双重校验锁,防止指令重排
    • 对实例添加 volatile,防指指令重排,对象读写到主存,保证线程可见。
    public class Singleton {
        private volatile static Singleton instance = null;
    	
        private Singleton(){}
    
        public static Singleton getInstance(){
            if(instance == null){
                synchronized (Singleton.class) {
                    if(instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    静态内部类

    • 静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
    • 外部类加载时,静态内部类不会立即加载,不占内存。
    • 外部类调用 getInstance() 方法时加载内部类,延迟实例化。
    • 静态内部类利用JVM类的加载机制保证初始化实例对象时线程互斥,线程安全。
    • JVM 保证一个类的()方法在多线程环境中被正确地加锁、同步:多个线程同时去初始化一个类,只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
    public class Singleton {  
        private Singleton() {} 
    	
        public static Singleton getInstance() {  
            return SingletonHolder.instance;  
        }  
    	
        private static class SingletonHolder{  
            private static Singleton instance = new Singleton();  
        }  
    }  
    

    枚举

    public enum Singleton {
    	// 定义一个枚举的元素代表实例
    	Instance;
    }
    
    

    全静态类

    • 静态类指 static class 修饰的类,静态类一定是静态内部类。
    • 好处:只能通过外部类访问。可以访问外部类的静态属性。
    • 把类中所有属性定义为静态也可以实现单例。
    • 静态类无需实例化即可使用,效率更高,在编译期就完成了静态绑定。
    • 失去面向对象优点:静态方法不能被覆写。但可以继承非 final 的单例类覆写其方法。
    • 静态类适用于简单固定、扩展少的场景。
    • 单例类最主要的优点:单例模式面向对象的特性更强,可以通过继承来实现多态,通过实现接口提供同一接口的不同实现。

    参考

    https://mp.weixin.qq.com/s?__biz=MzAwNDA2OTM1Ng==&mid=2453150113&idx=1&sn=848b13f729996e6c94478653818df528&scene=21#wechat_redirect

    https://blog.csdn.net/jike11231/article/details/106229415

    https://blog.csdn.net/mnb65482/article/details/80458571

    https://zhuanlan.zhihu.com/p/158928210

  • 相关阅读:
    在虚拟机安装Hadoop
    数据传输功能单元——DID参数定义
    AI人工智能小程序系统开发
    《编译原理》复习第1章~第5章
    【luogu P8354】多边形(容斥)(NTT优化DP)
    基于verilog的CRC校验(汇总)
    Vue 获取/设置指定组件高度
    MYSQL深入学习
    一分钟学一个 Linux 命令 - mv 和 cp
    Android入门第7天-TableLayout
  • 原文地址:https://blog.csdn.net/RiceVan/article/details/126956755