单例模式的N种写法

简介: 1.前言 写完这个题目,我感觉自己好像"孔乙己"啊,回字的四种写法要不要学啊~ 我们经常会用到单例模式,但是我对他一直没有一个统一的的认识,比如我清楚好多种单例的写法,但是每一种是怎么演化来的?具体解决了什么问题?这块就没有那么清晰了,因此此文对单例模式进行一个总结,同时手撸一下代码加深理解.

1.前言

写完这个题目,我感觉自己好像"孔乙己"啊,回字的四种写法要不要学啊~

我们经常会用到单例模式,但是我对他一直没有一个统一的的认识,比如我清楚好多种单例的写法,但是每一种是怎么演化来的?具体解决了什么问题?这块就没有那么清晰了,因此此文对单例模式进行一个总结,同时手撸一下代码加深理解.

2.介绍

单例模式,即某一个类在整个系统中有且仅有一个实例.

经常用来读取配置,获取连接等等.

3.实现思路

1.构造方法私有化.
2.提供静态的方法,返回唯一实例.

这块很好理解,要想保证只有唯一实例,构造方法就不能被别人调用,只能自己调用用来创建唯一的实例,同时,将构造方法私有化了,就需要对外提供一个访问点,以方便其他类获取这个实例.

4.具体实现

4.1 饿汉式

这种写法的优势就是,真的简单,基本就是的实现思路的耿直实现,代码如下:

public class HungrySingleton {
  private static HungrySingleton hungrySingleton = new HungrySingleton();
  private HungrySingleton() {
  }
  public static HungrySingleton getSingleton() {
    return hungrySingleton;
  }
}

这样子有个问题,就是只要这个类被加载了,那么就会创建出唯一实例,也不管用不用...

虽然其实工作中问题不大,但是学习嘛,就要吹毛求疵,我们要懒加载的方式!

4.2 懒汉式

代码如下:

public class LazySingleton {

  private static LazySingleton lazySingleton = null;

  private LazySingleton() {
  }

  public static LazySingleton getSingleton() {
    if (null == lazySingleton) {
      lazySingleton = new LazySingleton();
    }
    return lazySingleton;
  }
}

这种方式也挺好理解的,而且实现了懒加载!只有在调用的时候才创建实例,节省了好大的空间呢!(并不)

但是这种方式仍然是有问题的,那就是著名的你有现在问题两个了.

如果多个线程同时来请求获取实例,上面这种懒汉式是解决不了的,会提供多个实例,也就违背了单例模式的初衷了(多个线程同时进入判空语句).

4.3 的懒汉优化一下

不就是线程安全吗?把我知道的volatile和synchronized都用上!

public class LazySingleton2 {

  private static volatile LazySingleton2 lazySingleton = null;

  private LazySingleton2() {
  }

  public static LazySingleton2 getSingleton() {
    synchronized (LazySingleton2.class) {
      if (null == lazySingleton) {
        lazySingleton = new LazySingleton2();
      }
    }
    return lazySingleton;
  }

}

这种方法看起来没有问题了,用volatilew修饰了唯一实例,保证内存可见性,用synchronized加锁,每次只允许一个线程访问判空语句,这不就解决了上面的问题吗?

是的,杀鸡用牛刀也不一定做的好啊..想想判空语句以及里面的实例化的执行频率,从理想的情况来讲,只有第一次会执行创建实例,剩下的都是返回实例就完事了.

为了这一种情况,每次都加锁,,性能下降太厉害了(其实并不,加了锁我们大部分时间也是够用的).

那再优化一下.

4.4 双重检查锁

代码如下:

public class DoubleCheckSingleton {

  private static volatile DoubleCheckSingleton singleton = null;

  private DoubleCheckSingleton() {
  }

  public static DoubleCheckSingleton getSingleton() {
    if (null == singleton) {
      synchronized (DoubleCheckSingleton.class) {
        if (null == singleton) {
          singleton = new DoubleCheckSingleton();
        }
      }
    }
    //2
    return singleton;
  }

}

这就是传说中的双重检查锁了,说实话,这个代码看起来我觉得有点难看....

但是其实是比较好使的,大部分的获取实例请求都会直接来到//2位置,而极少量的为空进行加锁,保证线程安全.

双重检查锁对上一步的优化是:多添加一重判断,过滤掉大部分不需要加锁的操作,同时,加锁后再次进行判断,防止在第一次判断-加锁期间已经创建了实例.

4.5 静态内部类实现

public class InnerClassSingleton {

  private static class Holder {

    private static InnerClassSingleton singleton = new InnerClassSingleton();
  }

  private InnerClassSingleton() {
  }

  public static InnerClassSingleton getSingleton() {
    return Holder.singleton;
  }

}

我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:

4.6 枚举写法

上面的所有实现都有一点小问题:

  1. 序列化与反序列化没有考虑,每次反序列化都能拿到一个新的实例.
  2. 反射,都可以通过反射强行调用privite的构造方法.

这时候就是枚举类出现的时候了!

public enum EnumSingleton {
  SINGLETON;
}

在《Effective Java》最后推荐了这样一个写法,看起来简直简单的有点不可思议,那么它是怎么保证以上几点的呢?

  1. 枚举类的初始化过程天然线程安全.即保证了线程安全.
  2. 对枚举的序列化与反序列禁止了自定义,由JDK实现,不会出现反序列化多个实例的情况.

在 《Effctive Java》中,作者极力推荐枚举实现单例,甚至说了它是单例实现的最好写法.

虽然我还没有应用过枚举实现单例,但是很快我就会将它加进我的代码库里.

总结

在单例实现中,我们需要注意以下三个问题:

  1. (重要)延迟加载,避免浪费.
  2. (重要)线程安全,避免多个实例.
  3. 序列化安全.

完。









ChangeLog


2019-01-31 完成



以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

目录
相关文章
|
3天前
|
弹性计算 运维 搜索推荐
三翼鸟携手阿里云ECS g9i:智慧家庭场景的效能革命与未来生活新范式
三翼鸟是海尔智家旗下全球首个智慧家庭场景品牌,致力于提供覆盖衣、食、住、娱的一站式全场景解决方案。截至2025年,服务近1亿家庭,连接设备超5000万台。面对高并发、低延迟与稳定性挑战,全面升级为阿里云ECS g9i实例,实现连接能力提升40%、故障率下降90%、响应速度提升至120ms以内,成本降低20%,推动智慧家庭体验全面跃迁。
|
4天前
|
数据采集 人工智能 自然语言处理
3分钟采集134篇AI文章!深度解析如何通过云无影AgentBay实现25倍并发 + LlamaIndex智能推荐
结合阿里云无影 AgentBay 云端并发采集与 LlamaIndex 智能分析,3分钟高效抓取134篇 AI Agent 文章,实现 AI 推荐、智能问答与知识沉淀,打造从数据获取到价值提炼的完整闭环。
365 91
|
4天前
|
SQL 人工智能 自然语言处理
Geo优化SOP标准化:于磊老师的“人性化Geo”体系如何助力企业获客提效46%
随着生成式AI的普及,Geo优化(Generative Engine Optimization)已成为企业获客的新战场。然而,缺乏标准化流程(Geo优化sop)导致优化效果参差不齐。本文将深入探讨Geo专家于磊老师提出的“人性化Geo”优化体系,并展示Geo优化sop标准化如何帮助企业实现获客效率提升46%的惊人效果,为企业在AI时代构建稳定的流量护城河。
379 156
Geo优化SOP标准化:于磊老师的“人性化Geo”体系如何助力企业获客提效46%
|
5天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
4天前
|
数据采集 缓存 数据可视化
Android 无侵入式数据采集:从手动埋点到字节码插桩的演进之路
本文深入探讨Android无侵入式埋点技术,通过AOP与字节码插桩(如ASM)实现数据采集自动化,彻底解耦业务代码与埋点逻辑。涵盖页面浏览、点击事件自动追踪及注解驱动的半自动化方案,提升数据质量与研发效率,助力团队迈向高效、稳定的智能化埋点体系。(238字)
261 156
|
12天前
|
机器人 API 调度
基于 DMS Dify+Notebook+Airflow 实现 Agent 的一站式开发
本文提出“DMS Dify + Notebook + Airflow”三位一体架构,解决 Dify 在代码执行与定时调度上的局限。通过 Notebook 扩展 Python 环境,Airflow实现任务调度,构建可扩展、可运维的企业级智能 Agent 系统,提升大模型应用的工程化能力。