Featured image of post [设计模式] 单例模式实现

[设计模式] 单例模式实现

单例模式是一种常用的软件设计模式。定义是单例的类只能有一个实例对象,并向外提供一个全局访问点

什么是单例模式

单例模式是一种常用的软件设计模式。定义是单例的类只能有一个实例对象,并向外提供一个全局访问点。

优点

  • 只有一个实例对象,节省内存空间
  • 避免频繁的创建销毁对象,提高性能
  • 避免对共享资源的多重占用
  • 能够全局访问

实现思路

  1. 私有化构造方法,让外部不能 new 对象。
  2. 在类的内部创建好实例。【静态变量的目的是为了类加载的时候创建实例】
  3. 向外提供一个公有的静态方法,返回该唯一实例。

实现写法

1. 饿汉式

/**
 * @description: 饿汉式
 * @author: opoa
 * @create: 2021-03-14 20:29
 **/

/**
 * 饿汉式
 */
public class Singleton1 {

    /**
     * 私有化构造方法
     */
    private Singleton1() {}

    /**
     * 在类的内部创建好实例。【静态变量的目的是为了类加载的时候创建实例】
     */
    private final static Singleton1 instance = new Singleton1();

    /**
     * 向外提供一个公有的静态方法,返回该唯一实例。
     * @return 实例
     */
    public static Singleton1 getInstance() {
        return instance;
    }
}

优点:写法简单,在类加载的时候就创建了实例,避免了线程同步问题,线程安全。
缺点:如果实例一直没有被使用,则会造成内存空间的浪费。

2. 懒汉式

非线程安全

public class Singleton2 {

    private Singleton2() {}

    private static Singleton2 instance;

    public static Singleton2 getInstance() {

        // 如果实例为空 则初始化一个赋给 instance
        if (null == instance) {
            instance = new Singleton2();
        }
        return instance;
    }
}

懒汉式,顾名思义,采用懒加载的方式,当需要用到的时候才初始化实例。 这种写法在多线程运行时存在安全问题,可能有多个线程同时进入判断条件,生成多个实例。不推荐使用

线程安全

public class Singleton3 {

    private Singleton3() {}

    private static Singleton3 instance;

    public static synchronized Singleton3 getInstance() {
        if (null == instance) {
            instance = new Singleton3();
        }
        return instance;
    }

}

优点:线程安全。
缺点:效率低。

3. 双重检查锁

public class Singleton4 {

    private Singleton4() {}

    // volatile 对于 instance 所有的写将先于读
    private static volatile Singleton4 instance;

    public static Singleton4 getInstance() {

        // 第一次检查    只在第一次创建实例的时候同步,之后不再同步
        if (null == instance) {
            synchronized (Singleton4.class) {
                // 第二次检查   确保只有一个线程能进入
                if (null == instance) {
                    instance = new Singleton4();
                }
            }
        }
        return instance;
    }
}

volatile 关键字:线程始终能读取到 volatile 修饰的变量的最新值。 这种写法线程安全,懒加载,效率高,推荐使用

4. 枚举类

public enum Singleton5 {

    /**
     * 唯一实例
     */
    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }
}

测试代码:

public class TestSingleton5 {

    @Test
    public void test() {

        Singleton5 instance = Singleton5.INSTANCE;
        Singleton5 instance1 = Singleton5.INSTANCE;

        // 比较内存地址
        System.out.println(instance == instance1);
        // 比较 hashCode
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());

        instance.doSomething();
    }
}

运行结果:

true
1645995473
1645995473
doSomething

这种写法简单高效,充分利用了枚举类的特性。是绝对的单例,即使反序列化也不会生成多个实例。推荐使用

Tips

如何区分懒汉式和饿汉式

懒汉式:顾名思义,它比较懒,只有需要用到的时候才会去生产,毕竟 Deadline 才是唯一生产力嘛。这里也可以理解为懒加载的形式。

饿汉式:不愿意让自己多饿一分钟,宁愿先把饭做好了,饿了的话就可以直接吃饭。对应提前初始化好实例对象。

推荐等级

枚举类 > 双重检查锁 > 饿汉式

参考资料

【设计模式】单例模式的八种写法分析

使用 Hugo 构建 主题 StackJimmy 设计
发表了 33 篇文章・ 总计 66.74 k 字
本站总访问量 · 总访客数
本博客已稳定运行 🧡