写点什么

手撕单例的 5 种写法!

作者:王磊
  • 2024-12-03
    陕西
  • 本文字数:1950 字

    阅读完需:约 6 分钟

手撕单例的 5 种写法!

单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。当然,它也是面试中的常客,尤其是某手面试中经常要求应聘者手撕,所以今天咱们就来盘盘它。


单例模式的实现方式有很多,如下图所示:



具体实现如下。

1.饿汉式模式

此在饿汉式单例模式中,实例在类加载时就被创建,这种方式的优点是实现简单,线程安全(因为类加载过程是线程安全的)。缺点是可能会导致实例过早创建,如果实例创建过程比较耗时或者占用大量资源,而在程序运行初期并不需要该实例,就会造成资源浪费。


public class Singleton {    // 1.私有静态成员变量,在类加载时就创建实例    private static Singleton instance = new Singleton();
// 2.私有构造函数,防止外部通过构造函数创建实例 private Singleton() {}
// 3.公共静态方法,用于获取唯一的实例 public static Singleton getInstance() { return instance; }}
复制代码

2.懒汉模式(非安全)

懒汉式单例模式在第一次调用 getInstance 方法时才创建实例,这样可以避免实例过早创建。但上述代码是非线程安全的,在多线程环境下,可能会出现多个线程同时进入 if 语句,导致创建多个实例的情况。


public class Singleton {    // 1.私有静态成员变量,初始化为null    private static Singleton instance = null;
// 2.私有构造函数,防止外部通过构造函数创建实例 private Singleton() {}
// 3.公共静态方法,用于获取唯一的实例 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
复制代码

3.懒汉模式(安全效率低)

此版本的懒汉式单例模式通过在 getInstance 方法上添加 synchronized 关键字,使其成为线程安全的。但这种方式的缺点是每次调用 getInstance 时都需要获取锁,会导致性能下降,尤其是在高并发环境下。


public class Singleton {    // 1.私有静态成员变量,初始化为null    private static Singleton instance = null;
// 2.私有构造函数,防止外部通过构造函数创建实例 private Singleton() {}
// 3.公共静态方法,用于获取唯一的实例 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
复制代码

4.双重检查锁模式

双重检查锁定模式在懒汉式基础上进行了优化,通过两次检查 instance 是否为 null,既保证了在第一次需要实例时创建实例,又在一定程度上避免了每次调用 getInstance 都获取锁的情况,提高了性能。不过,由于指令重排序等问题,可能会导致一些错误,因此需要在 instance 变量前添加 volatile 关键字来解决。


public class Singleton {    // 1.私有静态成员变量,初始化为null    private volatile static Singleton instance = null;
// 2.私有构造函数,防止外部通过构造函数创建实例 private Singleton() {}
// 3.公共静态方法,用于获取唯一的实例 public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}
复制代码

5.静态内部类模式

这种方式利用了静态内部类的特性,当外部类被加载时,静态内部类不会被加载,只有当调用 getInstance 方法时,静态内部类才会被加载,此时才创建单例实例。这种实现方式既保证了线程安全,又避免了在不需要实例时过早创建实例,是一种比较常用的单例模式实现方式。


public class Singleton {    // 1.私有构造函数,防止外部通过构造函数创建实例    private Singleton() {}
// 2.静态内部类,其中包含单例实例 private static class SingletonHolder { private static final Singleton instance = new Singleton(); }
// 3.公共静态方法,用于获取唯一的实例 public static Singleton getInstance() { return SingletonHolder.instance; }}
复制代码

小结

单例模式虽然实现方式有 5 种:饿汉模式、懒汉非安全模式、懒汉安全模式、双重效验锁模式、静态内部类模式,但它的写法基本都是以下三步:


  1. 定义私有构造方法(防止 new 多个实例)。

  2. 定义私有变量(承接单例对象)。

  3. 定义统一返回对象的方法。


本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:并发编程、MySQL、Redis、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、JVM、设计模式、消息队列等模块。

用户头像

王磊

关注

javacn.site 2018-08-25 加入

我的小站:javacn.site

评论

发布
暂无评论
手撕单例的 5 种写法!_王磊_InfoQ写作社区