美文网首页工作生活
实用设计模式-Java单例模式

实用设计模式-Java单例模式

作者: DoubleFooker | 来源:发表于2019-07-04 13:38 被阅读0次

    常见单例模式

    1.饿汉式

    /**
    * 缺点:有现场创建资源,如果没用到造成内存浪费
    */
    public class HungrySingleton implements Serializable {
        /**
         * 私有化构造方法
         */
        private HungrySingleton() {
        }
        /**
         * 或者 静态代码块初始化
         * private static final HungrySingleton HUNGRY_SINGLETON;
         * static{
         * HUNGRY_SINGLETON = new HungrySingleton();
         * }
         */
        private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
        public static HungrySingleton getInstance() {
            return HUNGRY_SINGLETON;
        }
    }
    

    测试用例

    public class HungrySingletonTest {
    
        /**
         * 单例校验
         */
        @Test
        public void testSingleton() {
            Set obj = new ConcurrentSkipListSet();
            CountDownLatch countDownLatch = new CountDownLatch(100);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    HungrySingleton instance = HungrySingleton.getInstance();
                    if (obj.add(instance.toString())) {
                        System.out.println(instance.toString());
                    }
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }).start();
                countDownLatch.countDown();
            }
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    

    2.懒汉式

    public class LazySingleton implements Serializable {
        private LazySingleton() {
        }
    
        private static LazySingleton LAZY_SINGLETON;
    
        /**
         * synchronized 关键字添加锁,性能下降
         * @return
         */
        public static synchronized LazySingleton getInstance() {
            if (LAZY_SINGLETON == null) {
                LAZY_SINGLETON = new LazySingleton();
            }
            return LAZY_SINGLETON;
    
        }
    }
    

    3. Double Check 懒汉式

    public class DoubleCheckSingleton {
        private DoubleCheckSingleton() {
        }
    
        /**
         * volatile 关键字防止 JVM指令重排序
         */
        private volatile static DoubleCheckSingleton DOUBLECHECK_SINGLETON;
    
        public static DoubleCheckSingleton getInstance() {
            /**
             *  synchronized不加在方法上 确保只在初始化时同步
             *  但是如果不用volatile修饰,存在指令重排序问题
             *   DOUBLECHECK_SINGLETON = new DoubleCheckSingleton();可分解为指令
             * memory = allocate();   //1:分配对象的内存空间
             * ctorInstance(memory);  //2:初始化对象
             * instance = memory;     //3:设置instance指向刚分配的内存地址
             * JVM指令重排序后可能出现
             * memory = allocate();   //1:分配对象的内存空间
             * instance = memory;     //3:设置instance指向刚分配的内存地址,此时对象还没有被初始化!
             * ctorInstance(memory);  //2:初始化对象
             *
             */
            if (DOUBLECHECK_SINGLETON == null) {
                synchronized (DoubleCheckSingleton.class) {
                    if (DOUBLECHECK_SINGLETON == null) {
                        DOUBLECHECK_SINGLETON = new DoubleCheckSingleton();
                    }
                }
            }
            return DOUBLECHECK_SINGLETON;
        }
    }
    

    4.静态内部类

    public class InnerClassSingleton {
        private InnerClassSingleton() {
        }
    
        /**
         * 调用InnercClassHolder才创建实例
         *
         * @return
         */
        public static InnerClassSingleton getInstance() {
            return InnerClassHolder.LAZY;
        }
    
        private static class InnerClassHolder {
            private static final InnerClassSingleton LAZY = new InnerClassSingleton();
        }
    }
    

    5.枚举实现注册式

    Effective Java 推荐实现方式,枚举天生不支持反射创建,也不存在序列化破坏单例问题

    public enum RegSingleton {
        INSTANCE;
    
        public int doSomeing() {
            return INSTANCE.hashCode();
        }
    }
    

    测试用例

       @Test
        public void testSingleton() {
            Set obj = new ConcurrentSkipListSet();
            CountDownLatch countDownLatch = new CountDownLatch(100);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    if (obj.add(RegSingleton.INSTANCE.doSomeing())) {
                        System.out.println(RegSingleton.INSTANCE.doSomeing());
                    }
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }).start();
                countDownLatch.countDown();
            }
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    反射对单例的破坏

     /**
         * 反射破坏单例
         * 解决方案 在私有构造方法抛出异常
         */
        @Test
        public void refSingleton() {
            try {
                Class<?> clazz = HungrySingleton.class;
                Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(null);
                declaredConstructor.setAccessible(true);
                HungrySingleton instance1 = (HungrySingleton)       
                    declaredConstructor.newInstance();
                System.out.println(instance1);
                HungrySingleton instance2 = HungrySingleton.getInstance();
                System.out.println(instance2);
                System.out.println(instance1 == instance2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    解决方案

      /**
       * 解决反射破坏单例
       */
     private HungrySingleton() {
         if (HUNGRY_SINGLETON != null) {
             throw new RuntimeException("不允许反射创建实例");
         }
     }
    

    序列化对单例的破坏

        /**
         * 序列化破坏单例
         */
        @Test
        public void seriable() {
            try {
                HungrySingleton instance = HungrySingleton.getInstance();
                FileOutputStream out = new FileOutputStream("HungrySingleton");
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
                objectOutputStream.writeObject(instance);
                out.close();
                objectOutputStream.close();
                FileInputStream in = new FileInputStream(new File("HungrySingleton"));
                ObjectInputStream objectInputStream = new ObjectInputStream(in);
                HungrySingleton hungrySingleton = (HungrySingleton) 
                    objectInputStream.readObject();
                in.close();
                objectInputStream.close();
                System.out.println(instance);
                System.out.println(hungrySingleton);
                System.out.println(instance == hungrySingleton);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    解决方案,JDK在反序列化时会检测readResolve方法,通过复写方法防止新建实例。

        /**
         * 防止序列化破坏单例
         * JDK反序列化时 使用反射调用无参构造函数创建实例
         * @return
         */
        public Object readResolve() {
            return HUNGRY_SINGLETON;
        }
    

    相关文章

      网友评论

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

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