手撕单例的 5 种写法!

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

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

alt

具体实现如下。

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. 定义统一返回对象的方法。
#java#
Java面试精讲 文章被收录于专栏

Java常见面试题、场景题、企业真题精讲。

全部评论

相关推荐

敢逐云霄志:你打招呼语怎么能这么长,hr都没看下去的欲望,简明扼要说重点,就读于某某学校某某专业,26届应届毕业生,学信网可查,先后在某某公司实习过(如有),然后做过什么项目,想找一份什么样的工作,可实习几个月以上,期待您的回复。
点赞 评论 收藏
分享
头像
11-03 16:48
已编辑
百度_高级研发工程师
事实是检验真理的唯一标准。 无论我们怎么去说,去讲述,去证明,都抵不过一个offer来得实在,无论我们怎么去复现求职中的摸爬滚打、扒皮抽筋、狼狈不堪,都抵不过你在简历写上大厂的名字(外包不算)。 所以在我求职期间,我什么话都不说,什么话都不讲,因为没有意义,虽然我总讲过程才是意义,但只有当你上岸的那一刻,你才有资格回想在水里的挣扎,只有等你出了山,你才知道山的全貌。 我为什么一定要离开华为OD,难道它不稳定吗,不能赚钱吗。为了证明自己,那肯定有的。其实更多的是印证我的认知是否真的正确。 (给不了解我的人交代一下背景,在下双非一本,gap一年,华为OD外包,摸爬滚打4个月,艰难上岸百度正编)一、...
先锋战士:说得很真诚。鄙视链自古有之,学历,家庭背景,财富,权利。从小有之,小学羡慕那些当班委的,中学羡慕那些学生会的,高中羡慕尖子班拿教学金的,大学羡慕高绩点,毕业了羡慕进大厂的。工作了,又羡慕高职级的,再后来又羡慕别人早早结婚的。我想表达的观点很简单,无论是华为od还是百度,都是经历,没有孰高孰低,为了抵达下一个风景,总会付出更多东西,但不就是人生吗?正如登山,每个阶段的山,都要想办法攀登,在博主的文字中,见到了坚持和积极寻找问题解决办法的心态
学历对求职的影响
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务