【JavaEE初阶】 饿汉模式与懒汉模式详解与实现

简介: 【JavaEE初阶】 饿汉模式与懒汉模式详解与实现


🌴单例模式

单例模式是校招中最常考的设计模式之一.

那么啥是设计模式呢?

  • 设计模式好比象棋中的 “棋谱”. 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有一些固定的套路. 按照套路来走局势就不会吃亏.
  • 软件开发中也有很多常见的 “问题场景”. 针对这些问题场景, 大佬们总结出了一些固定的套路. 按照这个套路来实现代码, 也不会吃亏.

那么什么是单例模式呢?

  • 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
  • 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例

单例模式具体的实现方式, 又分成 “饿汉” 和 “懒汉” 两种

🍀饿汉模式

饿汉模式,就是它很饿,它的对象早早的就创建好了

这种方式比较常用,但容易产生垃圾对象。

类加载的同时, 创建实例

代码实现如下:

class Singleton {
    //创建新实例,static修饰,只创建一次
    private static Singleton instance = new Singleton();
    //构造方法进行封装,不能创建新实例
    private Singleton() {}
    //唯一实例的获取方法
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。

🎍懒汉模式

懒汉模式,顾名思义就是懒,没有对象需要调用它的时候不去实例化,有人来向它要对象的时候再实例化对象

🚩单线程版(线程不安全)

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。

因为没有加锁synchronized,所以严格意义上它并不算单例模式

代码实现如下:

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        //需要用到时进行创建
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

上面的懒汉模式的实现是线程不安全的.

  • 线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致
    创建出多个实例.

但是一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改instance 了)

🚩多线程版

所以我们对上述代码进行了优化

我们可以加上 synchronized 可以改善这里的线程安全问题.

优化代码如下:

class Singleton {
  private static Singleton instance = null;
  private Singleton() {}
  public synchronized static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}
  • 优点:第一次调用才初始化,避免内存浪费。
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率

该版的懒汉模式还存在一个问题,就是内存可见性的问题,不知道内存可见性的宝子可以去看看博主写的【JavaEE初阶】 volatile关键字 与 wait()方法和notify()方法详解里面对volati关键字部分的讲解

🚩多线程版(改进)

所以我们针对上述多线程版的懒汉模式进行了改进

以下代码在加锁的基础上, 做出了进一步改动:

  • 使用双重 if 判定, 降低锁竞争的频率.
  • 给 instance 加上了 volatile.

代码实现如下:

class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        //需要用到时进行创建
        if(instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

那么双重 if 判定 / volatile是怎么达到这样的效果的呢?

  • 加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.
  • 因此后续使用的时候, 不必再进行加锁了.
  • 外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.
  • 同时为了避免 “内存可见性” 导致读取的 instance 出现偏差, 于是补充上 volatile .
  • 当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,其中竞争成功的线程, 再完成创建实例的操作.
  • 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.

比如一下实例:

  1. 有三个线程, 开始执行 getInstance , 通过外层的 if (instance == null) 知道了实例还没有创建的消息. 于是开始竞争同一把锁

就像有三个好哥们都在追校花

  1. 其中线程1 率先获取到锁, 此时线程1 通过里层的 if (instance == null) 进一步确认实例是否已经创建. 如果没创建, 就把这个实例创建出来.

其中有个哥们就对校花说:你有男朋友吗?如果没有我做你男朋友吧,这时候其他哥们是在门口等待里面情况的

  1. 当线程1 释放锁之后, 线程2 和 线程3 也拿到锁, 也通过里层的 if (instance == null) 来确认实例是否已经创建, 发现实例已经创建出来了, 就不再创建了

校花答应了第一个哥们后,那哥们就走了

这时候另外一个哥们又来问:能做你男票不?

校花此时就说:我已经有男朋友了

此时这个哥们就不能成为校花的男朋友了

  1. 后续的线程, 不必加锁, 直接就通过外层 if (instance == null) 就知道实例已经创建了, 从而不再尝试获取锁了. 降低了开销

那哥们出去后就和追求校花的人说,校花已经有男盆友了

这时候等待的人听见这个消息就不会再进去询问了

⭕总结

关于《【JavaEE初阶】 饿汉模式与懒汉模式详解与实现》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

相关文章
|
6月前
|
设计模式 Java
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
56 2
|
1月前
|
设计模式 安全 Java
【多线程-从零开始-柒】单例模式,饿汉和懒汉模式
【多线程-从零开始-柒】单例模式,饿汉和懒汉模式
52 0
|
5月前
|
设计模式 存储 Java
JavaSE——面向对象高级一(2/4)-饿汉式单例、懒汉式单例、代码块、static的注意事项
JavaSE——面向对象高级一(2/4)-饿汉式单例、懒汉式单例、代码块、static的注意事项
31 0
|
6月前
|
安全 Java 编译器
【JavaEE初阶】 线程安全
【JavaEE初阶】 线程安全
|
6月前
|
设计模式 安全 编译器
多线程(初阶六:单例模式)
多线程(初阶六:单例模式)
54 0
|
6月前
|
设计模式 安全 Java
【JavaEE】多线程案例-单例模式
【JavaEE】多线程案例-单例模式
|
6月前
|
设计模式 安全 Java
【单例模式】饿汉式,懒汉式?JAVA如何实现单例?线程安全吗?
【单例模式】饿汉式,懒汉式?JAVA如何实现单例?线程安全吗?
64 0
|
设计模式 安全 Java
【Java|多线程与高并发】设计模式-单例模式(饿汉式,懒汉式和静态内部类)
设计模式是一种在软件开发中常用的解决复杂问题的方法论。它提供了一套经过验证的解决方案,用于解决特定类型问题的设计和实现。设计模式可以帮助开发人员提高代码的可重用性、可维护性和可扩展性。
|
设计模式 安全 Java
【JavaEE】Java设计模式-单例模式(饿汉式与懒汉式)
【JavaEE】Java设计模式-单例模式(饿汉式与懒汉式)
|
设计模式 安全 Java
常见面试题!6种单例设计模式的实现
枚举类天然防止反序列化。 其他单例模式 可以通过 重写 readResolve() 方法,从而防止反序列化,使实例唯一重写 readResolve() :
119 0
常见面试题!6种单例设计模式的实现