设计模式—创建型—单例模式

本文介绍了单例设计模式,包括其概念、优缺点以及多种实现方式,如饿汉式、懒汉式、双重校验、静态内部类和枚举方式。每种实现方式都考虑了线程安全和延迟加载,其中枚举方式被认为是最安全且简洁的实现方法。

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

创建性模式有五种:

1.工厂模式
    ①简单工厂模式(经常被用到,不属于GoF23种设计模式)
    ②工厂方法模式
    ③抽象工厂模式
2.建造者模式
3.原型模式
4.单例模式

单例设计模式

       单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

优缺点

优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。

2、避免对资源的多重占用(比如写文件操作)。

缺点:

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

代码实现方式:

1.饿汉式

       饿汉式的实现方式比较简单。就是在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。实现方式是不支持延迟加载,就是不管有没有使用它都已经创建好了,这也是有人觉得这种方式不太好的原因,如果实例占用资源多,比如占用内存多或初始化耗时长),提前初始化实例是一种浪费资源的行为。最好的方法应该在用到的时候再去初始化。反过来想如果初始化耗时长,那最好不要等到真正要用它的时候,才去执行这个耗时长的初始化过程,这会影响到系统的性能,比如导致响应时间过长。采用饿汉式实现方式,将耗时的初始化操作,提前到程序启动的时候完成,这样就能避免在程序运行的时候,再去初始化导致的性能问题。

public class IdGenerator { 
  private AtomicLong id = new AtomicLong(0);
  private static final IdGenerator instance = new IdGenerator();
  private IdGenerator() {}
  public static IdGenerator getInstance() {
    return instance;
  }
  public long getId() { 
    return id.incrementAndGet();
  }
}

2.懒汉式

懒汉式相对于饿汉式的优势是支持延迟加载,缺点很明显,因为加了synchronized导致其是串行执行,并发度很低

public class IdGenerator { 
  private AtomicLong id = new AtomicLong(0);
  private static IdGenerator instance;
  private IdGenerator() {}
  public static synchronized IdGenerator getInstance() {
    if (instance == null) {
      instance = new IdGenerator();
    }
    return instance;
  }
  public long getId() { 
    return id.incrementAndGet();
  }
}

在spring源码(如 ReactiveAdapterRegistry)和jdk源码(如 AbstractQueuedSynchronizer)中,有如下实现, 使用一个临时局部变量来接收,这样在判断instance!=null时,传统双检锁会访问2次主存;现在利用局部变量只需要访问1次主存,性能更好。

public class Singleton {
    private static volatile Singleton instance=null;
    private Singleton() {
    }

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

3.双重校验

        饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方式。在这种实现方式中,只要 instance 被创建之后,即便再调用 getInstance() 函数也不会再进入到加锁逻辑中了。所以,这种实现方式解决了懒汉式并发度低的问题。

public class IdGenerator { 
  private AtomicLong id = new AtomicLong(0);
  private static IdGenerator instance;
  private IdGenerator() {}
  public static IdGenerator getInstance() {
    if (instance == null) {
      synchronized(IdGenerator.class) { // 此处为类级别的锁
        if (instance == null) {
          instance = new IdGenerator();
        }
      }
    }
    return instance;
  }
  public long getId() { 
    return id.incrementAndGet();
  }
}

4. 静态内部类

利用 Java 的静态内部类。它有点类似饿汉式,但又能做到了延迟加载

public class IdGenerator { 
  private AtomicLong id = new AtomicLong(0);
  private IdGenerator() {}

  private static class SingletonHolder{
    private static final IdGenerator instance = new IdGenerator();
  }
  
  public static IdGenerator getInstance() {
    return SingletonHolder.instance;
  }
 
  public long getId() { 
    return id.incrementAndGet();
  }
}

SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。

5. 枚举最后

这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。具体的代码如下所示:


public enum IdGenerator {
  INSTANCE;
  private AtomicLong id = new AtomicLong(0);
 
  public long getId() { 
    return id.incrementAndGet();
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值