• 设计模式---单例模式


    定义:
    想确保任何情况下都绝对只有一个实例

    优点:
    1、在内存里只有一个实例,减少了内存开销
    2、可以避免对资源的多重占用
    3、设置全局访问点,严格控制访问

    缺点:没有接口,扩展困难

    重点:
    1、私有构造器
    2、线程安全
    3、延迟加载
    4、序列化和反序列化安全
    5、防止反射攻击

    实现方式:
    1、Double Check(双重检查锁)
    2、静态内部类

    Coding

    Double Check模式:

    public class LazyDoubleCheckSingleton {
    
        private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;
    
        private LazyDoubleCheckSingleton() {
        }
    
        public static LazyDoubleCheckSingleton getInstance() {
            if (lazyDoubleCheckSingleton == null) {
                synchronized (LazyDoubleCheckSingleton.class) {
                    if (lazyDoubleCheckSingleton == null) {
                        lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                        //1、分配内存给这个对象
                        //2、初始化对象
                        //3、设置lazyDoubleCheckSingleton 指向刚分配的内存地址
    
                        //如果存在指令重排序的话 即 1、3、2的话 会存在问题。
                    }
                }
            }
            return lazyDoubleCheckSingleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    volatile的作用就是防止指令重排序(如下图是重排序后的问题):
    在这里插入图片描述
    静态内部类:

    public class StaticInnerClassSingleton {
    
        private StaticInnerClassSingleton() {
        }
    
        public static StaticInnerClassSingleton getInstance() {
            return InnerClass.staticInnerClassSingleton;
        }
    
        private static class InnerClass{
    
            private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    饿汉式

    import java.io.Serializable;
    
    public class HungrySingleton implements Serializable {
    
        private final static HungrySingleton hungrySingleton;
    
        static {
            hungrySingleton = new HungrySingleton();
        }
    
        private HungrySingleton() {
        }
    
        public static HungrySingleton getInstance() {
            return hungrySingleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    单例模式的破坏:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class Test {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            HungrySingleton instance = HungrySingleton.getInstance();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
            oos.writeObject(instance);
    
            File file = new File("singleton_file");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            HungrySingleton newInstance = (HungrySingleton) ois.readObject();
            System.out.println(instance == newInstance);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    false
    
    • 1

    进行修改:

    import java.io.Serializable;
    
    public class HungrySingleton implements Serializable {
    
        private final static HungrySingleton hungrySingleton;
    
        static {
            hungrySingleton = new HungrySingleton();
        }
    
        private HungrySingleton() {
        }
    
        public static HungrySingleton getInstance() {
            return hungrySingleton;
        }
    
    	//新增方法
        public Object readResolve() {
            return hungrySingleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    再次执行:

    true
    
    • 1

    解析:

    Test.java	HungrySingleton newInstance = (HungrySingleton) ois.readObject();
    java.io.ObjectInputStream#readObject	Object obj = readObject0(false);
    java.io.ObjectInputStream#readObject0	case TC_OBJECT:return checkResolve(readOrdinaryObject(unshared));
    java.io.ObjectInputStream#readOrdinaryObject	obj = desc.isInstantiable() ? desc.newInstance() : null;//创建了新的实例
    java.io.ObjectInputStream#readOrdinaryObject	desc.hasReadResolveMethod()) //判断是否存在readResolve方法,(注释解读)
    java.io.ObjectInputStream#readOrdinaryObject	Object rep = desc.invokeReadResolve(obj); //调用readResolve方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    防止单例模式反射攻击:

    	//反射攻击
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<HungrySingleton> declaredConstructor = HungrySingleton.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            HungrySingleton instance = HungrySingleton.getInstance();
            HungrySingleton newInstance = declaredConstructor.newInstance();
            System.out.println(instance == newInstance);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    false
    
    • 1

    解决:

    
    import java.io.Serializable;
    
    public class HungrySingleton implements Serializable {
    
        private final static HungrySingleton hungrySingleton;
    
        static {
            hungrySingleton = new HungrySingleton();
        }
    
        private HungrySingleton() {
            //防止反射调用
            if (hungrySingleton != null) {
                throw new RuntimeException("单例构造器禁止反射调用");
            }
        }
    
        public static HungrySingleton getInstance() {
            return hungrySingleton;
        }
    
        //防止反序列化
        public Object readResolve() {
            return hungrySingleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注:双重检查锁 模式防止单例破坏 是无法做到的,我们可以通过定义flag==true,然后在构造方法中 判断flag是否为true,为true则设置为false,否则抛出异常, 但是反射还是可以动态修改flag的值,所以还是无法阻止单例破坏。

    枚举解决反射破坏单例:

    public enum  EnumInstance {
    
        INSTANCE;
    
        private Object data;
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public static EnumInstance getInstance() {
            return INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    枚举序列化校验:

        public static void main(String[] args) throws IOException, ClassNotFoundException {
            EnumInstance instance = EnumInstance.getInstance();
            instance.setData(new Object());
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
            oos.writeObject(instance);
    
            File file = new File("singleton_file");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            EnumInstance newInstance = (EnumInstance) ois.readObject();
            System.out.println(instance == newInstance);
            System.out.println(instance.getData() == newInstance.getData());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    true
    true
    
    • 1
    • 2

    枚举反射校验

        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<EnumInstance> declaredConstructor = EnumInstance.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            EnumInstance instance = EnumInstance.getInstance();
            EnumInstance newInstance = declaredConstructor.newInstance();
            System.out.println(instance == newInstance);
            System.out.println(instance.getData() == newInstance.getData());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //没有找到构造方法
    Exception in thread "main" java.lang.NoSuchMethodException: com.jiangzheng.course.dubbo.provider.pattern.creational.singleton.EnumInstance.<init>()
    	at java.lang.Class.getConstructor0(Class.java:3082)
    	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    	at com.jiangzheng.course.dubbo.provider.pattern.creational.singleton.Test.main(Test.java:47)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们在java.lang.Enum中找到了构造方法:

        protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
    
    • 1
    • 2
    • 3
    • 4

    我们重试修改测试方法:

        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<EnumInstance> declaredConstructor = EnumInstance.class.getDeclaredConstructor(String.class, int.class);
            declaredConstructor.setAccessible(true);
            EnumInstance instance = EnumInstance.getInstance();
            EnumInstance newInstance = declaredConstructor.newInstance("test", 666);
            System.out.println(instance == newInstance);
            System.out.println(instance.getData() == newInstance.getData());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //提示 枚举类型不能够被反射调用
    Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    	at com.jiangzheng.course.dubbo.provider.pattern.creational.singleton.Test.main(Test.java:50)
    
    • 1
    • 2
    • 3
    • 4

    彩蛋:

    jad的使用:
    登录网站下载工具:https://varaneckas.com/jad/

    枚举中声明方法

    public enum  EnumInstance {
    
        INSTANCE{
            protected void printTest(){
                System.out.println("print text");
            }
        };
    
        protected abstract void printTest();
    
        private Object data;
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public static EnumInstance getInstance() {
            return INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
        public static void main(String[] args) {
            EnumInstance.getInstance().printTest();
        }
    
    • 1
    • 2
    • 3

    基于容器的单例模式:

    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class ContainerSingleton {
    
        private static Map<String,Object> singletonMap = new ConcurrentHashMap<>();
    
        public static void putInstance(String key, Object instance) {
            if (StringUtils.isNoneEmpty(key) && instance != null) {
                if (singletonMap.containsKey(key)) {
                    singletonMap.put(key, instance);
                }
            }
        }
    
        public static Object getInstance(String key) {
            return singletonMap.get(key);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    基于ThreadLocal的单例模式

    //每个线程内部获取时 都是单例的
    public class ThreadLocalInstance {
        
        private static final ThreadLocal<ThreadLocalInstance> threadLocal = new ThreadLocal<ThreadLocalInstance>(){
            @Override
            protected ThreadLocalInstance initialValue() {
                return new ThreadLocalInstance();
            }
        };
    
        private ThreadLocalInstance() {
        }
    
        public static ThreadLocalInstance getInstance() {
            return threadLocal.get();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    JDK中Runtime中有使用饿汉式单例模式。
    Mybatis中的ErrorContext.java 采用的是ThreadLocal的模式单例

  • 相关阅读:
    分布式电源接入对配电网影响分析(Matlab代码实现)
    AI:77-基于深度学习的工业缺陷检测
    COCI2021-2022#1 Logičari
    Python学习笔记(04)
    matlab问题求解答
    linux设备模型:sysfs(kobject)解析
    Apple :苹果将在明年年底推出自己的 AI,预计将随 iOS 18 一起推出
    【Java 语言】读取 properties 配置文件 ( Java 语言中的 properties 配置文件 | 使用 properties 配置文件 )
    Linux有哪些指令
    如何用 Tana AI 一站式批量润色整理音频笔记?
  • 原文地址:https://blog.csdn.net/qq_36364521/article/details/125866894