美文网首页
设计模式-单例模式

设计模式-单例模式

作者: 东方欲晓_莫道君行早 | 来源:发表于2021-06-09 20:12 被阅读0次

    反编译工具jad
    下载地址:https://varaneckas.com/jad
    4、单例模式
    饿汉式,比较消耗内存。当程序需要创建大量单例时,会影响程序启动速度。

    package com.jdwa.singleton;
    
    public class HungrySingleton {
        private static final HungrySingleton instance= new HungrySingleton();
    
        private HungrySingleton(){}
    
        public static HungrySingleton getInstance(){
            return instance;
        }
    }
    
    

    简单的懒汉式,解决了内存消耗问题,但是存在线程安全问题。

    package com.jdwa.singleton;
    
    public class LazySingleton {
        private static LazySingleton instance;
    
        private LazySingleton(){}
    
        public static LazySingleton getInstance(){
            if (instance== null) {
                instance= new LazySingleton();
            }
            return instance;
        }
    }
    
    
    

    带synchronized关键字的懒汉式,解决了内存消耗以及线程安全问题,但是创建大量单例会因为锁而导致性能下降。

    package com.jdwa.singleton;
    
    public class LazySingletonWithSync {
        private static LazySingletonWithSync instance;
    
        private LazySingletonWithSync(){}
    
        public synchronized static LazySingletonWithSync getInstance(){
            if (instance== null) {
                instance= new LazySingletonWithSync();
            }
            return instance;
        }
    }
    
    

    双检索单例,在一定程度上解决了性能问题。

    package com.jdwa.singleton;
    
    public class LazyDoudleCheckSingleton {
        private static LazyDoudleCheckSingleton instance = new LazyDoudleCheckSingleton();
    
        private LazyDoudleCheckSingleton(){}
    
        public static LazyDoudleCheckSingleton getInstance(){
            if (instance == null) {
                synchronized (LazyDoudleCheckSingleton.class){
                    if (instance == null) {
                        instance = new LazyDoudleCheckSingleton();
                    }
                }
            }
            return instance;
        }
    }
    
    

    静态内部类,同时解决了上述问题,到那时会被通过反射创建。默认不加载静态内部类,只有使用的时候才会加载。

    package com.jdwa.singleton;
    
    public class LazyInnerClassSingleton {
        private LazyInnerClassSingleton(){}
        
        public static LazyInnerClassSingleton getInstance(){
            return LazyHolder.INSTANCE;
        }
        
        private static class LazyHolder {
            private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
        }
    }
    
    

    反射会破坏单例,以静态内部类举例

    package com.jdwa.singleton;
    
    import java.lang.reflect.Constructor;
    
    public class LazyInnerClassTest {
        public static void main(String[] args) throws Exception{
            Class<?> clazz = LazyInnerClassSingleton.class;
            Constructor constructor = clazz.getDeclaredConstructor();//返回无参的public和非public的
    
            constructor.setAccessible(true); //强制访问
            Object o1 = constructor.newInstance();
            Object o2 = constructor.newInstance();
            System.out.println(o1==o2); //false
        }
    }
    
    

    优化静态内部类:私有的构造器添加判断

    package com.jdwa.singleton;
    
    public class LazyInnerClassWithImProvedCon {
        private LazyInnerClassWithImProvedCon(){
            if (LazyHolder.INSTANCE != null) {
                throw new RuntimeException("不允许创建多个实例");
            }
        }
    
        public static LazyInnerClassWithImProvedCon getInstance(){
            return LazyInnerClassWithImProvedCon.LazyHolder.INSTANCE;
        }
    
        private static class LazyHolder {
            private static final LazyInnerClassWithImProvedCon INSTANCE = new LazyInnerClassWithImProvedCon();
        }
    }
    
    

    当单例模式支持序列化时,也可能被破坏,解决方案:加一个readResolve方法
    代码与测试如下:

    package com.jdwa.singleton;
    
    import java.io.*;
    
    public class SeriableSingleton implements Serializable {
        private final static SeriableSingleton INSTANCE = new SeriableSingleton();
        private SeriableSingleton(){}
    
        public static SeriableSingleton getInstance(){
            return INSTANCE;
        }
    
        private Object readResolve(){
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            SeriableSingleton s1 = null;
            SeriableSingleton s2 = getInstance();
    
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream("SeriableSingleton.obj");
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                oos.writeObject(s2);
                oos.flush();
                oos.close();
    
                FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
                ObjectInputStream ois = new ObjectInputStream(fis);
                s1 = (SeriableSingleton) ois.readObject();
                ois.close();
    
                System.out.println(s1 == s2); //true
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    

    通过该方法可以避免单例对象被序列化破坏,但是同样的,通过源码可以看出,该实例是被实例化了两次,只不过只返回了一次。当有大量单例对象被实例化,就会造成内存开销的增加。

    要解决这个问题,可以使用注册式单例。其思路是将每一个实例都登记到某一个地方,使用唯一标记符来获取单例。有两种方式:枚举式,容器式。
    枚举式单例:

    package com.jdwa.singleton;
    
    public enum EnumSingleton {
        INSTANCE;
        private Object data;
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
        
        public static EnumSingleton getInstance(){
            return INSTANCE;
        }
        
    }
    

    容器式:

    package com.jdwa.singleton;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class ContainerSingleton {
        private ContainerSingleton(){}
        private static Map<String,Object> ioc = new ConcurrentHashMap<>();
        public static Object getBean(String className){
            synchronized (ioc) {
                if (!ioc.containsKey(className)){
                    Object obj = null;
                    try {
                        obj = Class.forName(className).newInstance();
                        ioc.put(className,obj);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    return obj;
                } else {
                    return ioc.get(className);
                }
            }
        }
        
    }
    

    ThreadLocal 线程单例实现

    package com.jdwa.singleton;
    
    public class ThreadLocalSingleton {
        private static final ThreadLocal<ThreadLocalSingleton> threadlocalInstance = new ThreadLocal<ThreadLocalSingleton>(){
            @Override
            protected ThreadLocalSingleton initialValue(){
                return new ThreadLocalSingleton();
            }
        };
        
        private ThreadLocalSingleton(){}
        
        public static ThreadLocalSingleton getInstance(){
            return threadlocalInstance.get();
        }
    }
    

    相关文章

      网友评论

          本文标题:设计模式-单例模式

          本文链接:https://www.haomeiwen.com/subject/lkceeltx.html