设计模式之单例模式

单例模式

饿汉式、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 类内部防止反射和反序列化时破坏单例。

相关推荐
程序员Aries3 天前
从零开始实现一个高并发内存池_DayThree:内存池整体框架与ThreadCache、TLS无锁访问
c++·学习·单例模式
爱奥尼欧3 天前
【Linux】系统部分——线程安全与线程的单例模式
linux·安全·单例模式
青草地溪水旁3 天前
第一章:单例模式 - 武林中的孤高剑客
单例模式
huangyuchi.4 天前
【Linux实战 】Linux 线程池的设计、实现与单例模式应用
linux·c++·单例模式·线程池·懒汉模式·项目·linux系统
拧之5 天前
✅设计模式笔记
笔记·单例模式·设计模式
蓝莓味的口香糖6 天前
【JS】什么是单例模式
开发语言·javascript·单例模式
稚辉君.MCA_P8_Java10 天前
DeepSeek Java 单例模式详解
java·spring boot·微服务·单例模式·kubernetes
坐不住的爱码10 天前
单例模式入门
单例模式
CoderIsArt10 天前
四种对象型创建模式:抽象工厂、 build模式、原型ProtoType与单例模式
单例模式·原型模式
charlie11451419112 天前
精读C++20设计模式——创造型设计模式:单例模式
c++·学习·单例模式·设计模式·c++20