跟着GPT学设计模式之单例模式

这篇具有很好参考价值的文章主要介绍了跟着GPT学设计模式之单例模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

单例有几种经典的实现方式,它们分别是:饿汉式、懒汉式、双重检测、静态内部类、枚举。

单例模式解决的问题

  • 处理资源访问冲突,资源的访问因为并发带来的问题。通过限制类的实例化过程,单例模式确保在应用程序运行期间只会创建一个特定类的对象。这对于需要共享数据或资源的情况非常有用,避免了多个实例同时操作导致数据不一致或资源浪费的问题。
  • 表示全局唯一类,比如,配置信息类。单例模式提供了一个全局访问点,使得其他类可以轻松地访问该单例对象。这样可以方便地共享类的实例,避免了频繁地传递对象实例的麻烦。

带来的好处:

  • 在一些场景中能减少内存的使用和性能提升。
  • 解决资源访问互斥的问题。
  • 节省资源:由于单例模式只创建一个实例,可以节省系统资源,尤其是在需要频繁创建和销毁对象的场景下。
  • 维护一致性:单例模式可以确保数据和资源的一致性,因为只有一个实例进行操作,避免了多个实例之间的竞争和冲突。
  • 全局访问:单例模式提供了一个全局访问点,使得其他对象可以轻松地获取实例,方便了数据共享和交互。
  • 延迟实例化:单例模式可以延迟对象的实例化,只有在需要时才进行创建,提高了系统的性能和效率。

缺点:

  • 高耦合性:单例模式的实现通常需要在类中创建全局访问点,这导致了对象的使用者与单例类之间存在高度的耦合性。这样一来,当需要修改单例类时,可能需要修改引用该类的所有代码,增加了代码的维护难度。
  • 难以进行单元测试:由于单例模式在全局范围内共享实例,很难对单例对象进行模拟和替换,从而使得单元测试变得困难。单元测试应该是隔离的、独立的,而单例模式的全局特性会影响到测试结果,增加了测试的复杂性。
  • 对象生命周期过长:单例模式的对象在整个应用程序的生命周期中都存在,无法自动释放和回收。如果不正确地使用和管理单例对象,可能会导致内存泄漏或资源浪费的问题。
  • 不支持多线程并发访问:某些实现方式的单例模式在多线程环境下可能会出现并发访问的问题,需要进行额外的线程安全处理。例如,懒汉式需要在获取实例时进行同步处理,可能会影响性能。
  • 违反单一职责原则:单例模式通常承担了过多的职责,既要负责自身的逻辑功能,又要管理对象的生命周期和资源等。这样违反了单一职责原则,降低了代码的可读性和可维护性。

单例模式的实现

饿汉式

饿汉式是一种单例模式的实现方式,其特点是在类加载时就创建并初始化了单例对象,无论是否需要使用该对象。

  1. 类加载时即创建对象:在饿汉式中,单例对象的创建和初始化发生在类加载的过程中,因此在应用程序启动时就已经存在一个单例对象。
  2. 线程安全:饿汉式的实现方式保证了在多线程环境下的线程安全性。由于单例对象在类加载时就被创建,所以不存在多个线程同时访问和创建对象的情况,避免了并发访问导致的线程安全问题。
  3. 全局访问点:饿汉式通过静态变量提供了一个全局访问点,其他对象可以直接通过该变量获取单例对象,方便了对单例对象的使用和操作。
  4. 性能优化:饿汉式避免了每次获取单例对象时的实例化开销,因为对象在类加载时已经完成了实例化。这在某些场景下可以提高系统性能。

然而,饿汉式也存在一些缺点:

  1. 占用内存空间:由于在类加载时就创建了单例对象,所以该对象会一直存在于内存中,无论是否被使用。如果单例对象占用较大内存,可能会造成资源浪费。
  2. 强耦合性:饿汉式在类加载时就创建对象,导致单例对象与类的生命周期紧密耦合,难以灵活控制单例对象的创建和销毁。
  3. 延迟加载不支持:饿汉式无法实现延迟加载,即只有当需要使用单例对象时才进行实例化。这可能在某些场景下造成不必要的开销。

因此,在使用饿汉式时需要考虑以上优缺点,并根据具体需求进行选择。

package com.iluwatar.singleton;

/**
 * Singleton class. Eagerly initialized static instance guarantees thread safety. 单例类,在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。
 */
public final class IvoryTower {

  /**
   * Private constructor so nobody can instantiate the class.
   * 构造器私有,所以无法通过构造器创建这个类
   */
  private IvoryTower() {
  }

  /**
   * Static to class instance of the class.
   * 静态变量初始化,jvm加载的时候就会执行
   */
  private static final IvoryTower INSTANCE = new IvoryTower();

  /**
   * To be called by user to obtain instance of the class.
   * 通过这个方法获取这个类的实例
   * @return instance of the singleton.
   */
  public static IvoryTower getInstance() {
    return INSTANCE;
  }
}

懒汉式

懒汉式是单例模式的另一种实现方式。与饿汉式不同,懒汉式在需要时才进行单例对象的创建和初始化,也就是延迟加载。

以下是懒汉式的一般实现方式:

  • 私有化构造方法:将单例类的构造方法声明为私有,防止外部代码通过直接实例化来创建对象。
  • 提供静态方法获取实例:通过一个静态方法来获取单例对象,在该方法内部进行懒加载的处理。
  • 延迟加载:在静态方法内部,先判断单例对象是否已经被创建,如果未创建,则进行对象的实例化操作。否则,直接返回已创建的对象。
  • 线程安全处理:因为懒汉式在需要时才创建对象,所以存在多线程环境下可能同时进入创建实例的判断逻辑的情况。因此,需要在静态方法中增加同步控制,保证只有一个线程可以创建实例。

懒汉式相对于饿汉式有一些优点:

  • 延迟加载:懒汉式可以在需要时才进行实例化,避免了在应用程序启动时就创建对象,从而减少了不必要的资源消耗。
  • 线程安全控制:通过在静态方法中增加同步控制,可以保证在多线程环境下的线程安全性。

然而,懒汉式也存在一些缺点:

  • 性能开销:由于需要进行同步控制,懒汉式在获取单例对象时可能会引入性能开销,特别是在高并发环境下。
  • 双重检查锁机制问题:懒汉式通常使用双重检查锁机制来实现延迟加载和线程安全。但这种方式在某些编程语言和平台下可能存在问题,导致无法正确实现线程安全性。

因此,在使用懒汉式时需要注意解决线程安全性问题,并根据具体需求和场景综合考虑是否选择懒汉式作为单例模式的实现方式。


/**
 * <p>Thread-safe Singleton class. The instance is lazily initialized and thus needs synchronization
 * mechanism.</p>懒汉式相对于饿汉式的优势是支持延迟加载。增加了类锁。
 *
 */
public final class ThreadSafeLazyLoadedIvoryTower {

  private static volatile ThreadSafeLazyLoadedIvoryTower instance;

  private ThreadSafeLazyLoadedIvoryTower() {
    // Protect against instantiation via reflection
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /**
   * The instance doesn't get created until the method is called for the first time.实例不能初始化直到第一次调用完成,因为加了锁的。
   */
  public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
    if (instance == null) {
      instance = new ThreadSafeLazyLoadedIvoryTower();
    }
    return instance;
  }
}

双重检测

双重检测(Double-Checked Locking)是一种在懒汉式中用于实现延迟加载和线程安全的机制。它是为了解决懒汉式在多线程环境下可能出现竞态条件(Race Condition)而引入的。

双重检测的基本原理如下:

  • 在静态方法中首先进行一次判断,如果单例对象已经被创建,则直接返回该对象,避免不必要的同步开销。
  • 如果单例对象尚未创建,则进入同步块。
  • 在同步块内部再次进行判断,确保只有一个线程可以创建单例对象。
  • 在同步块内部创建单例对象,并将其赋值给静态变量。
  • 最后,返回单例对象。

通过双重检测机制,可以减少对同步锁的使用,提高性能。

需要注意的是,在某些编程语言和平台下,双重检测锁机制可能存在问题,即所谓的双重检测锁失效问题,导致无法正确实现线程安全性。为了解决这个问题,可以使用volatile关键字来确保可见性,或者使用其他线程安全的机制来实现延迟加载。

总之,双重检测是一种在懒汉式中常用的实现方式,可以在一定程度上解决线程安全和性能的问题,但需要注意在具体的编程语言和平台上是否适用,并仔细考虑是否需要额外的机制来确保线程安全性。


/**
 * <p>Double check locking.</p>
 * 既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方式
 * <p>http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html</p>
 *
 * <p>Broken under Java 1.4.</p>
 *
 * @author mortezaadi@gmail.com
 */
public final class ThreadSafeDoubleCheckLocking {

  private static volatile ThreadSafeDoubleCheckLocking instance;

  /**
   * private constructor to prevent client from instantiating.构造器私有防止客户端调用初始化
   */
  private ThreadSafeDoubleCheckLocking() {
    // to prevent instantiating by Reflection call抛出异常来防止反射调用
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /**
   * Public accessor.
   *
   * @return an instance of the class.
   */
  public static ThreadSafeDoubleCheckLocking getInstance() {
    // local variable increases performance by 25 percent
    // Joshua Bloch "Effective Java, Second Edition", p. 283-284

    var result = instance;
    // Check if singleton instance is initialized. 检查单例是否初始化,如果初始化了就直接返回
    // If it is initialized then we can return the instance.
    if (result == null) {
      // It is not initialized but we cannot be sure because some other thread might have
      // initialized it in the meanwhile. 多线程并发可能同时创建实例,这里需要加锁。
      // So to make sure we need to lock on an object to get mutual exclusion.
      synchronized (ThreadSafeDoubleCheckLocking.class) {
        // Again assign the instance to local variable to check if it was initialized by some
        // other thread while current thread was blocked to enter the locked zone.
        // If it was initialized then we can return the previously created instance
        // just like the previous null check. 双重检查机制,如果创建了则直接返回
        result = instance;
        if (result == null) {
          // The instance is still not initialized so we can safely
          // (no other thread can enter this zone)
          // create an instance and make it our singleton instance. 如果仍然没有创建则我们可以在此处创建一个对象并赋值。
          instance = result = new ThreadSafeDoubleCheckLocking();
        }
      }
    }
    return result;
  }
}

静态内部类

在单例模式中,使用静态内部类实现是一种常见且线程安全的方式。通过静态内部类的特性,可以实现延迟加载和线程安全的单例对象。通过静态内部类实现的单例模式具有延迟加载、线程安全和高效的特点。在需要使用单例对象的时候才会进行实例化,而且能够保证多线程环境下的线程安全性。


/**
 * <p>The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton
 * object in Java.</p>Initialize on demand holder习惯用法是在Java中创建延迟初始化的单例对象的一种安全方法。
 *
 * <p>The technique is as lazy as possible and works in all known versions of Java. It takes
 * advantage of language guarantees about class initialization, and will therefore work correctly
 * in all Java-compliant compilers and virtual machines.</p>
 * 该技术尽可能懒惰,适用于所有已知版本的Java。它利用了关于类初始化的语言保证,因此将在所有符合Java的编译器和虚拟机中正确工作。
 * <p>The inner class is referenced no earlier (and therefore loaded no earlier by the class loader)
 * than the moment that getInstance() is called. Thus, this solution is thread-safe without
 * requiring special language constructs (i.e. volatile or synchronized).</p>
 *
 */
public final class InitializingOnDemandHolderIdiom {

  /**
   * Private constructor.
   */
  private InitializingOnDemandHolderIdiom() {
  }

  /**
   * Singleton instance.
   *
   * @return Singleton instance
   */
  public static InitializingOnDemandHolderIdiom getInstance() {
    return HelperHolder.INSTANCE;
  }

  /**
   * Provides the lazy-loaded Singleton instance.
   */
  private static class HelperHolder {
    private static final InitializingOnDemandHolderIdiom INSTANCE =
        new InitializingOnDemandHolderIdiom();
  }
}

枚举

使用枚举实现单例模式的优点包括:

  • 简洁明了:枚举实现单例模式非常简洁,只需声明一个枚举值,即可获得唯一实例。
  • 线程安全:枚举类型的实例是在类加载时初始化的,因此保证了线程安全性。
  • 序列化和反序列化安全:枚举类默认实现了Serializable接口,因此枚举单例在进行序列化和反序列化时,能够正确地保持实例的唯一性。
/**
 * <p>Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18</p>
 *
 * <p>This implementation is thread safe, however adding any other method and its thread safety
 * is developers responsibility.</p>这种创建单例的方法是线程安全的,但是添加其他方法的线程安全问题需要开发者处理。
 */
public enum EnumIvoryTower {

  INSTANCE;

  @Override
  public String toString() {
    return getDeclaringClass().getCanonicalName() + "@" + hashCode();
  }
}

以上内容基于GPT创建和整理。文章来源地址https://www.toymoban.com/news/detail-744036.html

参考

  • 设计模式Java实现
  • 设计模式之美-王争

到了这里,关于跟着GPT学设计模式之单例模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计模式之单例模式(懒汉, 饿汉)

    单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实例对象, 通过单例模式的方法创建的类在当前进程中只有一个实例对象. 常见的单例模式有两种: 饿汉式, 这里的 “饿” 意义表述不够清晰, 用 “急” 来表述意义更加容易联想一些

    2024年02月22日
    浏览(52)
  • Java设计模式之单例模式

    定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 想确保任何情况下都绝对只有一个实例 例如:线程池,数据库连接池一般都为单例模式 单例模式优点 在内存中只有一个实例,减少内存开销 可以避免对资源的多重占用 设置全局访问点,严格控制访问

    2024年02月02日
    浏览(72)
  • 浅谈设计模式之单例模式

    单例模式属于创建型模式,它提供了一种创建对象的最佳方式。单例模式指的是 单一的一个类 ,该类负责创建自己的对象,并且保证该 对象唯一 。该类提供了一种访问其唯一对象的方法,外部需要调用该类的对象可以通过方法获取,不需要实例化类的对象。 关键点: 单例

    2024年02月16日
    浏览(70)
  • c++设计模式之单例模式

    一个类无论创建多少对象 , 都只能得到一个实例 如上述代码中,我们通过new运算符创建出了类A的三个对象实例,而我们现在要做的是,如何设计类A,使得上述代码运行之后永远只产生同一个对象实例         我们知道,一个类对象是通过这个类的构造函数创建的,因此

    2024年01月19日
    浏览(57)
  • C#--设计模式之单例模式

    单例模式大概是所有设计模式中最简单的一种,如果在面试时被问及熟悉哪些设计模式,你可能第一个答的就是单例模式。 单例模式的实现分为两种: 饿汉式:在静态构造函数执行时就立即实例化。 懒汉式:在程序执行过程中第一次需要时再实例化。 两者有各自适用的场景

    2024年02月14日
    浏览(46)
  • C#设计模式之单例模式

    单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点。 单例模式的结构图如下所示: 对一些类来说,只有一个实例是很重要的。如何才能保证一个类只有一个实例并且这个实例易于被访问呢? 基于程序员之间的约定或是利用全局变量吗? 虽然这样

    2024年02月03日
    浏览(51)
  • Unity设计模式之单例模式

    单例模式(Singleton)是设计模式中很常见的一种设计模式,目的是为了让一个类在程序运行期间有且仅有一个实例,且方便全局访问。 1、私有的构造函数。 2、含有一个该类的静态私有对象。 3、静态的公有函数或属性,方便用户创建或者获取它本身的静态私有对象。 当项目

    2023年04月09日
    浏览(71)
  • 万字解析设计模式之单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个 单一 的类,该类负责创建自己的对象,同时确保 只有单个对象被创建 。这个类提供了一种访问其唯一的对象的方式,

    2024年02月08日
    浏览(49)
  • 【Java 设计模式】创建型之单例模式

    在软件开发中,单例模式是一种常见的设计模式, 它确保一个类只有一个实例,并提供一个全局访问点 。单例模式在需要控制某些资源,如数据库连接池、线程池等共享资源的情况下非常有用。在本文中,我们将介绍 Java 设计模式中的单例模式,了解其实现方式、使用场景

    2024年01月18日
    浏览(55)
  • 深入理解设计模式-创建型之单例模式

    如果有些数据在系统中应该且只能保存一份,那就应该设计为单例类。 如:配置类:在系统中,我们只有一个配置文件,当配置文件被加载到内存之后,应该被映射为一个唯一的【配置实例】,此时就可以使用单例,当然也可以不用。 全局计数器:我们使用一个全局的计数

    2024年02月12日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包