手撕单例的 5 种写法!

简介: 手撕单例的 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、设计模式、消息队列等模块。

相关文章
|
人工智能 前端开发 JavaScript
【炫技的代码写法】
【炫技的代码写法】
|
3月前
|
JavaScript
震惊了,面试官居然问我ES6中class语法糖是怎么实现的
【10月更文挑战第2天】震惊了,面试官居然问我ES6中class语法糖是怎么实现的
震惊了,面试官居然问我ES6中class语法糖是怎么实现的
|
8月前
|
JSON 前端开发 JavaScript
【JavaScript】面试手撕深拷贝(2),2024年最新nacos面试题及答案
【JavaScript】面试手撕深拷贝(2),2024年最新nacos面试题及答案
【JavaScript】面试手撕深拷贝(2),2024年最新nacos面试题及答案
|
7月前
|
Java Apache Spring
面试官:如何自定义一个工厂类给线程池命名,我:现场手撕吗?
【6月更文挑战第3天】面试官:如何自定义一个工厂类给线程池命名,我:现场手撕吗?
42 0
|
8月前
|
JSON JavaScript 前端开发
【JavaScript】面试手撕深拷贝(1),面试前必看的一本书书籍是什么
【JavaScript】面试手撕深拷贝(1),面试前必看的一本书书籍是什么
|
8月前
|
存储 JavaScript 前端开发
【JavaScript】面试手撕浅拷贝
引入 浅拷贝和深拷贝应该是面试时非常常见的问题了,为了能将这两者说清楚,于是打算用两篇文章分别解释下深浅拷贝。 PS: 我第一次听到拷贝这个词,有种莫名的熟悉感,感觉跟某个英文很相似,后来发现确实Copy的音译,感觉这翻译还是蛮有意思的
79 6
|
8月前
|
JavaScript 前端开发 索引
【JavaScript】面试手撕数组原型链(易)
续借上文,这篇文章主要讲的是数组原型链相关的考题,有些人可能会纳闷,数组和原型链之间有什么关系呢?我们日常使用的数组forEach,map等都是建立在原型链之上的。举个🌰,如我有一个数组const arr = [1,2,3]我想要调用arr.sum方法对arr数组的值进行求和,该如何做呢?我们知道数组没有sum函数,于是我们需要在数组的原型上定义这个函数,才能方便我们调用,具体代码如下。接下来我们就是采用这种方式去实现一些数组常用的方法。
73 6
|
8月前
|
前端开发 JavaScript 算法
【JavaScript】面试手撕数组排序
这章主要讲的是数组的排序篇,我们知道面试的时候,数组的排序是经常出现的题目。所以这块还是有必要进行一下讲解的。笔者观察了下前端这块的常用算法排序题,大概可以分为如下
56 2
|
机器学习/深度学习 自然语言处理 算法
程序员的炫技代码写法
程序员的炫技代码写法
|
设计模式 安全 Java
java设计模式之单例设计模式的妙用
1.设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美地解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因
91 1