设计模式:再谈单例模式

2023-11-04

单例模式可以说是每个人问设计模式都会脱口而出的几个设计模式之一,为什么之前写过一次了,现在我又写一遍。肯定不是重新写一遍怎么是设计模式。

这次写的单例模式主要围绕的关键词有,延迟加载,高并发,线程安全,为什么说用枚举的单例模式优雅且怎么做到的。。。。

借用争哥这个id生成器的例子:
先来5个实现单例模式的方式
(1)饿汉模式
(2)懒汉模式
(3)双重检测
(4)静态内部类
(5)枚举

饿汉(我不管怎么样,我上来就new一个对象,不能饿着我)


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();
  }
}

懒汉(用到的时候才去new对象,这里要注意跟我以前写的不一样这个是线程安全的懒汉写法)


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();
  }
}

(3)双重检测(就是我不想饿汉一样上来就new对象,想做到延迟加载,能省一点空间就省一点,但又不像懒汉一样,想支持高并发-----------于是搞了个双重检测,但是他有自己的问题就是指令重排的问题)


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)静态内部类(这个可以认为是上一个“双重检测”的改写,双重检测问题在于,可能多个线程,刚把这个id生成器还没new出来,指令重拍别的线程可能就先用上了。就是new 使用对象这两个操作1和2被打乱顺序了,解决方案除了可以用voliate这种增加线程间可见性,防止顺序被打乱以外。可以通过内部类来解决,加载本类的时候才会加载内部类,但不会生成对象,只有本类的方法调用时才能获取到。)


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();
  }
}

5 枚举 (枚举这个可太有意思了,这个枚举不太了解可以看一下这几个
美国知乎关于class,interface ,enum的区别
算是利用了枚举的特性:
An enum is a special “class” that represents a group of constants (unchangeable variables, like final variables).


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

谈一谈单例模式不好的地方?
单例模式反模式?我们使用单例模式都是为了解耦增加扩展性,单例这个就显得有点特立独行。

单例对 OOP 特性的支持不友好:他也不是不支持这OOP的封装、抽象、继承、多态,但你单独使用单例模式实现就会很怪

隐藏类之间的关系,可读性降低:单例直接返回一个对象,你就用就完了,但是这个会隐藏我们对于类与类之间的关系,可读性很差,我不想跳到一个类一堆方法里一步一步去跳转找到最后在哪创建的。

不支持有参数的构造函数,不灵活针对前面1,2,3点的一个补充。

单例模式的替代方案?
简单的是静态方法,觉得不够灵活,那就工厂模式,IOC容器(spring)

集群的单例模式?
我们谈的单例模式指的是进程间的单例,举个例子一个项目两台机都启动,能保证线程唯一吗?明显不能,因为两台机同一个项目属于不同的进程,类似的就是集群情况。我们前面这些都是保证,同一进程多线程的单例唯一的。

怎么来做,只能依靠外部力量,就是我们取一个对象,哪个进程来了先对他加个锁,看了小争哥的代码,他是用一个file这种竞争资源型的来解决,我觉得最好还是redis尤其是redission这种看门狗来实现。

这边贴的是小争哥的伪代码,对资源加锁


public class IdGenerator {
  private AtomicLong id = new AtomicLong(0);
  private static IdGenerator instance;
  private static SharedObjectStorage storage = FileSharedObjectStorage(/*入参省略,比如文件地址*/);
  private static DistributedLock lock = new DistributedLock();
  
  private IdGenerator() {}

  public synchronized static IdGenerator getInstance() 
    if (instance == null) {
      lock.lock();
      instance = storage.load(IdGenerator.class);
    }
    return instance;
  }
  
  public synchroinzed void freeInstance() {
    storage.save(this, IdGeneator.class);
    instance = null; //释放对象
    lock.unlock();
  }
  
  public long getId() { 
    return id.incrementAndGet();
  }
}

// IdGenerator使用举例
IdGenerator idGeneator = IdGenerator.getInstance();
long id = idGenerator.getId();
idGenerator.freeInstance();

有多例模式吗?
肯定有,但是也是限制的,3例模式就是最多3个这种
代码演示也借用别人的,大概意思就是类似饿汉模式,搞了三个塞进Map里面然后随机取


public class BackendServer {
  private long serverNo;
  private String serverAddress;

  private static final int SERVER_COUNT = 3;
  private static final Map<Long, BackendServer> serverInstances = new HashMap<>();

  static {
    serverInstances.put(1L, new BackendServer(1L, "192.134.22.138:8080"));
    serverInstances.put(2L, new BackendServer(2L, "192.134.22.139:8080"));
    serverInstances.put(3L, new BackendServer(3L, "192.134.22.140:8080"));
  }

  private BackendServer(long serverNo, String serverAddress) {
    this.serverNo = serverNo;
    this.serverAddress = serverAddress;
  }

  public BackendServer getInstance(long serverNo) {
    return serverInstances.get(serverNo);
  }

  public BackendServer getRandomInstance() {
    Random r = new Random();
    int no = r.nextInt(SERVER_COUNT)+1;
    return serverInstances.get(no);
  }
}

这回单例应该算是完整了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式:再谈单例模式 的相关文章

  • Spring控制器是线程安全的吗

    我遇到了这个控制器示例 想知道它是否是线程安全的 我特别想知道 gson 实例变量 import org springframework stereotype Controller import org springframework we
  • 用 @DataJpaTest 注释的测试不是用 @Autowired 注释的自动装配字段

    我有一个 Spring Boot 应用程序 其中包含 Spring Data Jpa 存储库 我需要围绕这个存储库运行单元 或组件 测试 我对 Spring Data Jpa 没有太多经验 这是我的测试 这很简单 我无法让它通过 impor
  • MP3:一种以毫秒为单位获取任何给定字节位置的位置的方法?

    我创建了一个 servlet 它返回从客户端请求的任何给定字节位置开始的流 来自 MP3 文件 这允许客户端在任何给定字节位置立即开始播放 而无需进行任何本地查找 现在 我有一个滑块可以直观地显示进度 我正在使用当前字节位置来更新滑块 但是
  • 使用 Checkstyle Plugin 时从插件调用代码时出现问题:“org.eclipse.jface”

    我正在尝试在 Rational Software Architect 7 0 0 4 上使用 eclipse cs 插件 我最近卸载了旧的 beta2 版本并安装了 beta3 插件本身按照之前的配置工作 但是每当我尝试通过 Windows
  • 如何对 IntStream 进行逆序排序

    我正在使用 txt 文件读取数字BufferedReader 我想颠倒该流中元素的顺序 以便在收集它们时 它们将从最高到最低排列 我不想在构建数组后进行排序 因为我不知道其中可能有多少元素 我只需要最高的 N 个元素 in new Buff
  • MI设备中即使应用程序被杀死,如何运行后台服务

    您好 我正在使用 alaram 管理器运行后台服务 它工作正常 但对于某些 mi 设备 后台服务无法工作 我使用了服务 但它无法工作 如何在 mi 中运行我的后台服务 MI UI有自己的安全选项 所以你需要的不仅仅是上面提到的粘性服务 你需
  • 场景生成器删除 fxml 文件中的导入

    我使用场景构建器 Gluon Scene Builder JavaFX Scene Builder 8 1 1 来创建应用程序的 UI 并使用 Eclipse 开发 JavaFX 现在 每次我在场景生成器中保存某些内容时 它都会从 fxml
  • 如何将 XMP XML 块序列化为现有的 JPEG 图像?

    我有许多 JPEG 图像 其中包含损坏的 XMP XML 块 我可以轻松修复这些块 但我不确定如何将 固定 数据写回图像文件 我目前正在使用 JAVA 但我愿意接受任何能让这项任务变得容易的事情 这是目标关于 XMP XML 的另一个问题
  • cucumber-junit-platform-engine 中的功能文件发现

    In cucumber junit我使用的库 CucumberOptions定义功能文件位置 package com mycompany cucumber import cucumber api CucumberOptions import
  • 使用 Guava 联合两个 ImmutableEnumSets

    我想联合两个ImmutableEnumSets来自番石榴 这是我的尝试 public final class OurColors public enum Colors RED GREEN BLUE YELLOW PINK BLACK pub
  • 具有多种值类型的 Java 枚举

    基本上我所做的是为国家编写一个枚举 我希望不仅能够像国家一样访问它们 而且还能够访问它们的缩写以及它们是否是原始殖民地 public enum States MASSACHUSETTS Massachusetts MA true MICHI
  • 为什么 ConcurrentHashMap::putIfAbsent 比 ConcurrentHashMap::computeIfAbsent 更快?

    使用 ConcurrentHashMap 我发现computeIfAbsent 比putIfAbsent 慢两倍 这是简单的测试 import java util ArrayList import java util List import
  • tomcat 过滤所有 web 应用程序

    问题 我想对所有网络应用程序进行过滤 我创建了一个过滤器来监视对 apache tomcat 服务器的请求 举例来说 它称为 MyFilter 我在 netbeans 中创建了它 它创建了 2 个独立的目录 webpages contain
  • Apache Commons CLI:替代已弃用的 OptionBuilder?

    IntelliJ 显示此示例代码中不推荐使用 OptionBuilderhttp commons apache org proper commons cli usage html http commons apache org proper
  • 如何在keycloak中动态编辑standalone.xml文件

    我正在尝试通过 docker 编辑standalone xml 并尝试添加 但 keycloak 正在使用它standalone xml 但我可以看到standalone xml 文件中的更改 我需要在standalone xml 文件中添
  • 如何在android sdk上使用PowerMock

    我想为我的 android 项目编写一些单元测试和仪器测试 然而 我遇到了一个困扰我一段时间的问题 我需要模拟静态方法并伪造返回值来测试项目 经过一些论坛的调查 唯一的方法是使用PowerMock来模拟静态方法 这是我的 gradle 的一
  • 阻止 OSX 变音符号为所有用户禁用 Java 中的 KeyBindings?

    注 我知道这个问题 https stackoverflow com questions 40335285 java keybinds stop working after holding down a key用户必须输入终端命令才能解决此问
  • 我想要一个 Java 阿拉伯语词干分析器

    我正在寻找阿拉伯语的 Java 词干分析器 我找到了一个名为 AraMorph 的库 但它的输出是无法控制的 并且它会形成不需要的单词 还有其他阿拉伯语词干分析器吗 这是新的阿拉伯语词干分析器 Assem 的阿拉伯语轻词干分析器 http
  • Java中有类似分支/跳转表的东西吗?

    Java有类似分支表或跳转表的东西吗 分支表或跳转表是 根据维基百科 http en wikipedia org wiki Branch table 用于描述使用分支指令表将程序控制 分支 转移到程序的另一部分 或可能已动态加载的不同程序
  • 使用 eclipse IDE 配置 angularjs

    我想开始使用 AngularJs 和 Java Spring 进行开发 我使用 Eclipse 作为 IDE 我想配置我的 Eclipse 以使这些框架无缝工作 我知道我可能要求太多 但相信我 我已经做了很多研究 你们是我最后的选择 任何帮

随机推荐

  • 文心一言实测:什么是文心一言?

    我不了解百度 能用五百字向我介绍这个公司吗 百度是一家全球领先的互联网公司 致力于为人们提供便捷 高效 智能的信息获取和交流服务 公司成立于2000年1月 总部位于中国北京 百度在搜索引擎 AI技术 云计算等领域拥有强大的技术实力和丰富的产
  • 为什么要学习spring cloud

    不论是商业应用还是用户应用 在业务初期都很简单 我们通常会把它实现为单体结构的应用 但是 随着业务逐渐发展 产品思想会变得越来越复杂 单体结构的应用也会越来越复杂 这就会给应用带来如下的几个问题 代码结构混乱 业务复杂 导致代码量很大 管理
  • Buuctf[极客大挑战 2019]LoveSQL

    题目打开是登录框 先尝试用万能密码 开始还以为这样就欧克了 但尝试之后发现这与flag无关 所以还是从sql开始走一遍 先查询一下字段 order by 4 字段数是四的时候报错了 后来小瑶尝试了在字段数为三时未报错 所以确定字段数是3 之
  • Interval【对偶图优化最小割(最大最小定理 周冬)】

    2020牛客多校第二场I题 首先 我们考虑最小割的方式来处理该问题 很明显的 这就是一张对偶图了 因为它没有任意两线会存在相交的可能了 所以根据对偶图的做法 我们可以将最小割问题转化为最短路了 绿色和粉色是新的对偶图所构成的边和点 然后我们
  • 软件安全测试报告模板_软件质量保证与测试(课后习题与答案)--仅供复习参考...

    第1章 软件测试基础 一 填空题 1 软件从 出生 到 消亡 的过程称为软件生命周期 2 早期的线性开发模型称为 瀑布 开发模型 3 引入风险分析的开发模型为 螺旋开发模型 4 ISO IEC9126 1991标准提出的质量模型包括 功能性
  • webpack5之loader配置(四)

    总述 要明白为什么要引入loader 因为webpack只能理解js和json这样的文件 这是webpack开箱可用的自带能力 loader可以让 webpack去解析其他类型的文件 并且将这些文件转化为有效的模块以供我们应用程序使用 如何
  • 国产CAE的涅槃-岩土行业高性能离散元软件MatDEM

    作者 刘春博士 一 导读 2019年9月11日 ANSYS公司公开宣称 收购LSTC公司 一举获得其旗下拥有LS DYNA 结构 流体 电磁的多物理场求解器 LS PrePost 前后处理器 LS OPT LS TASC 参数优化 拓扑优化
  • 微信小程序——关于图片

    属性 类型 默认值 必填 说明 src string 否 图片资源地址 mode string scaleToFill 否 图片裁剪 缩放的模式 webp boolean false 否 默认不解析 webP 格式 只支持网络资源 lazy
  • 我在训练maskrcnn模型识别羊的时候发现,用了1500张图片,模型的损失是逐渐降低的,预测框标注的很好,但是掩膜图层识别效果非常差,resnet使用的是101,详细说出为什么这种原因,并列出详细的...

    在训练 Mask R CNN 模型识别羊时 用了 1500 张图片 模型的损失是逐渐降低的 说明模型在训练过程中较好地学习了预测框标注的特征 然而 掩膜图层识别效果非常差 这可能是由以下几种原因造成的 数据不足 如果训练数据中羊的掩膜图层标
  • QT环境搭建:解决Linux系统下QT(qDebug和console)无输出

    在Linux下调试时突然发现无论怎样qDebuge无法输出了 寻找了半天 发现是环境变量出了问题 我的环境是统信的UOS 解决办法就是修改QT LOGGING RULES变量 1 查看是否是这个变量的问题 export p grep QT
  • linux各种版本下载地址

    http blog chinaunix net uid 26760055 id 3138203 html 首先提供两个镜像站 http mirrors sohu com http mirrors 163 com Red Hat Enterp
  • linux ubuntu18.04安装(更新)cmake版本 (坑是真滴多,我都给你趟过去了)

    笔者就是想学一手网络编程 通过本地clion编写代码 在云服务器上跑起来 这就涉及到了cmake版本问题 本地编译器cmake版本为3 22 3 云服务器的版本过于低了 用这个语句装的版本为3 10 好多人都不建议用这个装 但是这个不装的话
  • 简单的整理一下VIM环境配置和插件安装

    http www zhaiqianfeng com 2017 02 install vim plugins html 先占个坑 周末有时间再写
  • Java Stream流

    目录 1 Stream 流的获取方法 2 Stream 流的常见中间方法 3 Stream 流的常见终结方法 4 Stream 流的收集方法 获取 Stream 流 创建一条流水线 并把数据放到流水线上准备进行操作 中间方法 流水线上的操作
  • nginx篇10-限速三剑客之limit_req

    本文主要是对nginx官方limit req相关模块的限速原理的解释和一些个人理解 主要参考的文章为Rate Limiting with NGINX and NGINX Plus和nginx的ngx http limit req modul
  • C++编码规范(1):代码注释

    当你阅读别人的代码时如果没有注释那会是件比较痛苦的事 一说到注释我们马上想到是通过 或 这样来添加一些描述信息 这只是狭义的注释 广义的注释我们可以理解为 任何有助于理解代码的信息都可以看成注释 我们可以把写代码和写文章类比下 自然语言会有
  • Qt - 获取屏幕分辨率

    欢迎转载 请注明出处 https blog csdn net qq 39453936 spm 1010 2135 3001 5343 原文链接 https blog csdn net qq 39453936 article details
  • MySQL必知必会——第十七章组合查询

    组合查询 本章讲述如何利用UNION操作符将多条SELECT语句组合成一个结果集 组合查询 多数SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句 MySQL也允许执行多个查询 并将结果作为单个查询结果返回 这些组合查询通常称
  • chatgpt提问句式整理

    你不知道 GPT知道 1 元问题 我想了解xxxx 我应该向你问哪些问题 2 请给我列出xxx领域 行业相关的 最常用的50个概念 并做简单解释 如果有英文缩写 请给出完整的英文解释 3 请详细介绍一下elon musk的主要生平事迹 请详
  • 设计模式:再谈单例模式

    单例模式可以说是每个人问设计模式都会脱口而出的几个设计模式之一 为什么之前写过一次了 现在我又写一遍 肯定不是重新写一遍怎么是设计模式 这次写的单例模式主要围绕的关键词有 延迟加载 高并发 线程安全 为什么说用枚举的单例模式优雅且怎么做到的