设计模式之单例模式

单例模式

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

相关推荐
rainFFrain2 小时前
单例模式与线程安全
linux·运维·服务器·vscode·单例模式
UpUpUp……19 小时前
特殊类的设计/单例模式
开发语言·c++·笔记·单例模式
卡戎-caryon1 天前
【Linux网络与网络编程】03.UDP Socket编程
linux·服务器·网络·笔记·单例模式·udp·网络通信
菲fay2 天前
Unity 单例模式写法
unity·单例模式
并不会2 天前
多线程案例-单例模式
java·学习·单例模式·单线程·多线程·重要知识
Debug 熊猫2 天前
【Java基础】10章、单例模式、final关键字的使用技巧和使用细节、单例模式-懒汉式、单例模式-饿汉式【3】
java·javascript·后端·单例模式
每次的天空3 天前
Android 单例模式全解析:从基础实现到最佳实践
android·单例模式
xyliiiiiL3 天前
单例模式详解
java·开发语言·单例模式
三金C_C4 天前
单例模式解析
单例模式·设计模式·线程锁
小宋要上岸4 天前
设计模式-单例模式
单例模式·设计模式