设计模式之单例模式

单例模式

饿汉式、DCL懒汉式

饿汉式:

java 复制代码
//饿汉式单例
public class Hungry {

    private Hungry() {

    }

    //类加载的时候就会创建出实例对象,可能造成不必要的内存浪费
    private final static Hungry hungry = new Hungry();

    public static Hungry getInstance() {
        return hungry;
    }
}

DCL懒汉式:

java 复制代码
package GoF设计模式.single;

//懒汉式单例
public class Lazy {

    private Lazy() {

    }

    private static volatile Lazy lazy;

    //DCL懒汉式,只有调用方法的时候,才会去实例化对象
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                   lazy = new Lazy();
                    /* lazy = new Lazy();这一步操作,实际上在底层分为三步
                     * 1、分配内存空间
                     * 2、执行构造方法,初始化对象
                     * 3、把这个对象指向者个空间
                     * 因此,可能出现指令重排的情况
                     *
                     * 我们希望的执行顺序是:123
                     * 但如果出现指令重排:132
                     *
                     * 就会出现线程A执行先执行1、3,此时单例对象lazy还未被初始化
                     *
                     * 这时候线程B进来了,判断lazy不为空,就会返回一个未初始化的对象! //此时lazyMan还没有完成构造
                     * 因此,要加上volatile关键字防止指令重排
                     * */
                }
            }
        }
        return lazy;
    }
}

枚举类实现:

java 复制代码
//枚举类实现单例
public enum Singleton {
    //枚举类是天生的单例对象,这个Singleton对象INSTANCE,天生就是单例的。不需要考虑什么线程问题、反射问题...
    INSTANCE;

    public void doSomething(){
        System.out.println("单例对象调用该方法...");
    }
}
java 复制代码
    public static void main(String[] args) throws Exception {
        //饿汉式和懒汉式没必要说明怎么使用了,直接调用getInstance方法就可以获取实例

        //稍微说明一下枚举类
        //正常枚举类单例模式使用
        Singleton instance = Singleton.INSTANCE;
        instance.doSomething();
        //如果在多线程的情况下也是不会有问题的
        for (int i = 0; i < 10;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Singleton.INSTANCE.hashCode());
                }
            }).start();
        }

        //尝试反射创建两个不同的INSTANCE
        Constructor<Singleton> declaredConstructor
                = Singleton.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        Singleton instance2 = declaredConstructor.newInstance();
        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
    }

从打印结果可以看到,JVM会抛出异常,不能通过反射创建一个枚举类对象。


总结

(1)单例模式常见的写法有两种:懒汉式、饿汉式

(2)懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题

(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;

(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题

(6)为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序

(7)最优雅的实现方式是使用枚举,其代码精简,没有线程安全问题,且 Enum 类内部防止反射和反序列化时破坏单例。

相关推荐
小王子10243 小时前
设计模式Python版 单例模式
python·单例模式·设计模式
快乐非自愿4 小时前
「全网最细 + 实战源码案例」设计模式——单例设计模式
java·单例模式·设计模式
言之。11 小时前
【面试题Java】单例模式
java·开发语言·单例模式
小兜全糖(xdqt)1 天前
python中单例模式
开发语言·python·单例模式
等一场春雨1 天前
Java 设计模式 二 单例模式 (Singleton Pattern)
java·单例模式·设计模式
binbinxyz2 天前
设计模式:构建高效且可维护的软件系统的基石
单例模式·设计模式
magic 2453 天前
设计模式之单例模式
java·单例模式·设计模式
你读书了吗?4 天前
设计模式-----单例设计模式
单例模式·设计模式
wy02_5 天前
【设计模式】 单例模式(单例模式哪几种实现,如何保证线程安全,反射破坏单例模式)
java·单例模式·设计模式
蒙娜丽宁5 天前
【Python】深入探讨Python中的单例模式:元类与装饰器实现方式分析与代码示例
开发语言·python·单例模式