JAVA设计模式-创建型-单例模式

1、单例模式

    知识点:
    1、模式定义:一个类只有一个实例,提供一个全局的访问点
       应用场景:重量级别的对象,不需要多个实例,如线程池,数据库连接池等
       类图分析:实例对象a 和 一个getInstance方法
        
    2、字节码知识,字节码指令重排序
        通过javap -v a.class 查看指令过程

        创建对象(会涉及到指令排序),会有四个步骤
        1 分配对象的内存空间
        2 初始化对象
        3 设置instance指向刚分配的内存地址
        4 返回
        
        指令顺序可能会经过JVM和CPU的优化,指令重排(2,3步骤可能会错乱导致会出现多个实例)
        
        解决指令重排的方法,volatile关键字,会让JVM的顺序按照指定顺序执行
        
    3、类加载机制
        3.1、加载二进制数据到内存中,生成对应的Class数据结构
        3.2、连接:a 验证(class是否符合jvm规范) b 准备(给类的静态变量赋值) c 解析
        3.3、初始化:给类的静态变量赋初始值
        
        真正使用类时,才会进行初始化
        
    4、JVM序列化机制
    
        静态内部类--需要实现readResolve()方法,才支持反序列化
            
            实现serializable接口,并生成序列id(不生成的话,就会导致序列化和反序列化的对象不相同)
            
            //序列化-该对象必须实现serializable
            objectOutPutStream.writeObject(对象实例A)  
            //反序列化
            A = objectOutPutStream.readObject();
            默认情况下,序列化和反序列化两个对象是不相同的
                要想一致,对象A必须要新增readResolve()方法(serializable接口中说明),返回单例对象A
        
        枚举--本身就支持反序列化,不需要添加任何操作
        
        
    5、单例模式在spring框架 和  JDK源码中的应用
    
    
    
    6、五种实现方式
        
        1.1、懒汉式,用到才会new对象----延迟加载,不安全(1、方法级别锁,影响性能,2、代码块锁)
            static A a;
            A static getInstance(){  if(A == null) { a = new A();}}
            
        1.2、饿汉式,先new对象
        
        1.3、上述都是线程不安全的-----双重检测机制-线程安全-懒加载-不能防止反射
            A static getInstance(){             
                if(A == null) { 
                    synchronized(A.class){     
                        if(A == null) { 
                            a = new A();
                        }
                    }    
                }
                return a;
            }
            双重检测机制,以上还有一个隐藏漏洞
                通过javap -v a.class 查看指令过程
                
                创建对象涉及到指令重排
                    instance = new Singleton() 这行代码会被编译器译成如下JVM指令
                    memory = allocate() // 1 分配对象的内存空间
                    ctorInstance(memory) // 2 初始化对象
                    instance = memory // 3 设置instance指向刚分配的内存地址
                
                指令顺序可能会经过JVM和CPU的优化,指令重排(2,3步骤可能会错乱)
                    memory = allocate() // 1 分配对象的内存空间
                    instance = memory // 3 设置instance指向刚分配的内存地址
                    ctorInstance(memory) // 2 初始化对象
                
                这样就导致不为null时,对象还没初始化完成,这样会返回一个未初始化完成的对象
                
                
            解决方案:需要在instance对象的前面增加一个volatile修饰词
                volatile关键字,会让JVM的顺序按照指定顺序执行
                
        1.4、静态内部类实现-线程安全-懒加载-不能防止反射
            private static class Holder{
                private static final Singleton INSTANCE = new Singleton()
            }
            
            private Singleton(){}
            
            public static Singleton getInstance(){
                return Holder.INSTANCE;
            }
            
            内部使用classloader来保持线程安全的
            
            缺陷:可以通过反射,创建对个对象(1、获取构造器 2、设置构造器可用 3、newInstance方法构造对象)

            如何阻止反射:
                1、枚举
                2、在默认构造方法中,再次判断实例是否为空,不为空就自定义异常,防止反射调用
            
        1.5、枚举-线程安全-不是懒加载-能防止反射------------优先使用
            public class Singleton {
                public enum SingletonEnum{
                    INSTANCE;
                    private Singleton singleton;
                    
                    private SingletonEnum(){
                        System.out.print("初始化单例")
                        singleton = new Singleton();
                    }
                    
                    private Singleton getInstance(){
                        return singleton;
                    }
                }
                
                private Singleton(){}
                
                public static Singleton getInstance(){
                    return SingletonEnum.INSTANCE.getInstance();
                }
                
            }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值