美文网首页工作生活
实用设计模式-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设计模式 - 单例模式

    JAVA设计模式 - 单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • Java中单例模式你用的哪一种?

    一起讨论java中的单例模式。单例模式是java设计模式中算是最简单的设计模式了。 * java实现单例模式的写法...

  • JAVA设计模式之单例模式

    JAVA设计模式之单例模式

  • 设计模式

    Java 设计模式情景分析 ——单例模式 Java 设计模式情景分析——建造者模式 Java 设计模式情景分析——...

  • Java设计模式教程

    Java设计模式教程 Java工厂设计模式 Java抽象工厂模式 Java单例模式 Java建造者(Builder...

  • 设计模式——单例模式

    设计模式——单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型...

  • 设计模式

    单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式...

  • 设计模式《一》单例模式

    单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属...

  • 设计模式之单例模式详解(附源代码)

    单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属...

网友评论

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

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