设计模式之单例模式,看这篇文章就够了

本文详细解析了单例模式的四种常见实现:懒汉模式、并发安全的懒汉模式、双重检查锁定、饿汉模式、静态内部类和枚举。讨论了各自的优缺点,以及如何选择在实际项目中的应用策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式是23种设计模式中最简单、最常见的设计模式之一,许多人学习设计模式,第一个接触到的就是单例模式。

单例模式属于创建型模式,其目的是在当前进程中只创建一个实例,也可能是一个线程中属于单例。

想要写出安全又简洁的单例模式,如果不注意细节,很容易有潜在的bug,本文总结了最常见的4种单例模式写法,并分析每种优缺点。

懒汉模式

懒汉模式,就是在用的时候才会被创建,体现的是一种延迟加载的思想。

public class Singleton {      private static Singleton instance;      private Singleton (){}      public static Singleton getInstance() {          if (instance == null) {              instance = new Singleton();          }        return instance;      }  }  

这种写法lazy loading很明显,但是致命的是在多线程环境下不能正常工作,再来看看并发安全的懒汉模式。

public class Singleton {      private static Singleton instance;      private Singleton (){}      public static synchronized Singleton getInstance() {          if (instance == null) {              instance = new Singleton();          }          return instance;      }  }  

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是synchronized加锁之后,每次获取单例都需要上锁,所以效率很低,99%情况下不需要同步。再看下它的优化版:

public class Singleton {      private volatile static Singleton singleton;      private Singleton () {}      public static Singleton getSingleton() {          if (singleton == null) {              synchronized (Singleton.class) {                  if (singleton == null) {                      singleton = new Singleton();                  }            }        }        return singleton;      }  }  

这就是大名鼎鼎的双重检查锁方式,也有人质疑这种单例模式的可靠性,这里笔者不做过多讲解,有兴趣的伙伴可以去网上搜索:double-checked locking is broken。

需要注意的是,singleton实例变量必须加volatile关键字。加volatile,这里主要使用了它的有序性特性,可以禁止指令重排。有许多的博客作者说是利用了volatile的可见性,并不是的。

singleton = new Singleton();

是由三个步骤组成的:

  1. 为对象分配内存

  2. 实例化对象

  3. 将引用指向对应的内存地址


第2,3步可能发生指令重排列,第一个线程先将singleton指向一个未实例化对象的内存地址,然后再进行实例化对象。


若此时第二个线程进行第一个非空判断时,则为false,会直接返回还没有实例化对象的内存地址,从而可能产生空指针异常。

饿汉模式

饿汉模式就是在使用前,实力已经被创建了。其实现代码如下:

public class Singleton {      private static Singleton instance = new Singleton();      private Singleton (){}      public static Singleton getInstance() {          return instance;      }  }  

这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化,显然没有达到lazy loading的效果,也有变种写法。​​​​​​​

public class Singleton {      private Singleton instance = null;      static {          instance = new Singleton();      }      private Singleton () {}    public static Singleton getInstance() {      return this.instance;      }  }  

静态内部类

这是我在代码中比较常用的一种写法。​​​​​​​

public class Singleton {    private static class SingletonHolder {        private SingletonHolder() {}        private static final Singleton INSTANCE = new Singleton();        public static final Singleton getInstance() {            return INSTANCE;        }    }    public static Singleton getSingleton() {        return SingletonHolder.getInstance();    }}

或者:

public class Singleton2 {    private Singleton2() {}    private static final class Singleton2Holder {        public static final Singleton2 INSTANCE = new Singleton2();    }    public static Singleton2 getInstance() {        return Singleton2Holder.INSTANCE;    }}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,

这种方式与饿汉模式不同的是,是Singleton类被装载了,instance不一定被初始化,有lazy loading效果。

枚举

枚举和静态代码块的特性相似,使用枚举时,构造方法会被自动调用,也可以利用这个特性实现单例模式,不过比较少见。​​​​​​​

public class EnumSingleton{    private EnumSingleton(){}    public static EnumSingleton getInstance(){        return Singleton.INSTANCE.getInstance();    }        private static enum Singleton{        INSTANCE;                private EnumSingleton singleton;        //JVM会保证此方法绝对只调用一次        private Singleton(){            singleton = new EnumSingleton();        }        public EnumSingleton getInstance(){            return singleton;        }    }}
public static void main(String[] args) {    EnumSingleton obj1 = EnumSingleton.getInstance();    EnumSingleton obj2 = EnumSingleton.getInstance();    System.out.println("obj1==obj2?" + (obj1==obj2));  // true}

总结:

单例模式主要有懒汉模式、饿汉模式、内部类、基于枚举,4种写法,基于内部类的方式比较常见,也是比较推荐的方式。

设计模式单纯的使用比较简单,在许多复杂的业务场景,常常会把多种模式混合起来使用,比如单例模式+工厂模式。而且重要的是融会贯通,不在于死记硬背,这样才能灵活多变的应用。

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值