JAVA设计模式之单例模式

2023-05-16

转载http://blog.csdn.net/jason0539

概念:
  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
  单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


一、懒汉式单例

[java]  view plain  copy  print ?
  1. //懒汉式单例类.在第一次调用的时候实例化自己   
  2. public class Singleton {  
  3.     private Singleton() {}  
  4.     private static Singleton single=null;  
  5.     //静态工厂方法   
  6.     public static Singleton getInstance() {  
  7.          if (single == null) {    
  8.              single = new Singleton();  
  9.          }    
  10.         return single;  
  11.     }  
  12. }  
Singleton通过将构造方法限定为private避免了类在外部被实例化,

就是通过Singleton.getInstance()来构造Singleton实例,而不允许通过new  Singleton ()来构造。
在方法内部Singleton自己可以访问到自己的private方法的。  
在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:


1、在getInstance方法上加同步

[java]  view plain  copy  print ?
  1. public static synchronized Singleton getInstance() {  
  2.          if (single == null) {    
  3.              single = new Singleton();  
  4.          }    
  5.         return single;  
  6. }  

2、双重检查锁定

[java]  view plain  copy  print ?
  1. public static Singleton getInstance() {  
  2.         if (singleton == null) {    
  3.             synchronized (Singleton.class) {    
  4.                if (singleton == null) {    
  5.                   singleton = new Singleton();   
  6.                }    
  7.             }    
  8.         }    
  9.         return singleton;   
  10.     }  

3、静态内部类

[java]  view plain  copy  print ?
  1. public class Singleton {    
  2.     private static class LazyHolder {    
  3.        private static final Singleton INSTANCE = new Singleton();    
  4.     }    
  5.     private Singleton (){}    
  6.     public static final Singleton getInstance() {    
  7.        return LazyHolder.INSTANCE;    
  8.     }    
  9. }    
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。


二、饿汉式单例

[java]  view plain  copy  print ?
  1. //饿汉式单例类.在类初始化时,已经自行实例化   
  2. public class Singleton1 {  
  3.     private Singleton1() {}  
  4.     private static final Singleton1 single = new Singleton1();  
  5.     //静态工厂方法   
  6.     public static Singleton1 getInstance() {  
  7.         return single;  
  8.     }  
  9. }  
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

饿汉式和懒汉式区别

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:


1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。



2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

至于1、2、3这三种实现又有些区别,

第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。


什么是线程安全?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。



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

JAVA设计模式之单例模式 的相关文章

  • 使用 PowerMock 和 Mockito 模拟 Logger 和 LoggerFactory

    我想模拟以下记录器 但要验证日志条目是否被调用 而不是内容 private static Logger logger LoggerFactory getLogger GoodbyeController class 我想模拟用于 Logger
  • 为什么使用 Hibernate 和 Oracle 10g 方言通过 JPA 创建名为 hibernate_sequence 的序列?

    我所有的实体都使用这种类型 Id Id GeneratedValue strategy GenerationType SEQUENCE generator MYENTITY SEQ SequenceGenerator name MYENTI
  • 正则表达式匹配 Java 中的未转义逗号

    问题描述 我试图使用 String 类提供的 split 方法将 a 拆分为单独的字符串 文档告诉我 它将围绕参数的匹配进行拆分 参数是正则表达式 我使用的分隔符是逗号 但逗号也可以转义 我使用的转义字符是正斜杠 只是为了让事情变得更容易
  • 线程运行如何启动?

    我正在寻找一个关于线程的小例子 为了创建线程 我们可以通过两种方式来实现 Runnable接口或通过扩展Thread 我使用第一种方式 package test public class test implements Runnable p
  • 如何重写 Lombok Setter 方法

    我在我的项目和生成中使用 lombokSetters and Getters using Setters and GettersPOJO 类之上的注释 我正在尝试覆盖属性的 setters 方法 但它不起作用 我想检查 JSON 属性是 E
  • 媒体对象上的 javafx UNKNOWN 持续时间

    我是 Java 和 JavaFX 的新手 过去几年我一直在使用 QT 在 Python 上进行开发 现在我正在使用 Java 和 JavaFX 进行开发 我正在开发一个程序 可以为用户设定的时间播放音乐文件 然后停止 因此 我需要从媒体对象
  • 使用应用程序属性的 @Cacheable 条件

    我正在尝试将 Redis 与 Spring 一起使用 Cacheable但需要根据 Spring Boot 样式应用程序属性有条件地打开或关闭缓存 我的第一次尝试似乎不起作用 application properties 文件 auth t
  • 在 Maven 存储库中查找 Oracle JDBC 驱动程序

    我想将 oracle jdbc 驱动程序作为依赖项 运行时范围 添加到我的项目中 ojdbc14 在 MVNrepository 站点中 放入 POM 的依赖项是
  • 当不读取带有 URL 的 QR 码时,zxing QRCodeReader 中出现 ChecksumException

    如果我扫描带有 URL 的 QR 码 以下代码可以完美且快速地运行 然而 如果我用简单的字符串或数字序列解码 QR 码 这就是我想要做的 它有时会随机工作 但 99 的情况下它会失败并出现 ChecksumException if webc
  • 将 float 转换为 Short,精度损失最小[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我有一个生成浮点值 例如 0 37885 的正弦波 但我希望它们作为短裤 直接使用 Short 进行转换 得到的值为 0 那么解决方案是什么
  • 使用 == 比较 Long 对象类型和原始 int

    我有一个通过调用返回 Long 对象数据类型的方法 resp getResultCode 我想比较一下HttpStatus GONE value 它实际上只返回一个原始 int 值410 Long 会拆箱自身以正确地与 int 原语进行比较
  • Spring Boot数据休息中的日期问题

    当我处理日期时 我遇到了春季数据休息的问题 简而言之 就是推迟一天的日期 例如 如果我有 1111 11 11 它会返回给我 1111 11 10 SO 中有一些相关的帖子 ex1 https stackoverflow com quest
  • 从 android 将用户注册到 QuickBlox 用户

    我正在尝试在我的 Android 应用程序中使用 QuickBlox 我阅读了指南并导入了示例 一切正常 我更改了一些用户可以使用 EditText 作为用户名和另一个密码登录的内容 并且效果很好 但现在我想添加一个注册按钮 使用户能够注册
  • 基于区域设置的 SimpleDateFormat 模式,但强制使用 4 位数年份

    我需要建立一个像这样的日期格式dd MM yyyy 几乎就像DateFormat SHORT 但包含 4 个年份数字 我尝试用它来实现它 new SimpleDateFormat dd MM yyyy locale format date
  • Jersey/JAX-RS :在响应标头中返回内容长度而不是分块传输编码

    我正在使用 Jersey 创建 RESTful API 资源 并且ResponseBuilder生成响应 RESTful 资源的示例代码 public class infoResource GET Path service id Produ
  • 我正在从数组列表中获取内存地址,需要信息

    我正在获取一个文本文件并填充一个数组列表 为了测试该文件 我在继续之前将其打印出来 我只能看到内存地址 而看不到文件中的实际信息 我缺少一些简单且可能明显的东西吗 public class TriviaQuestion private St
  • 以编程方式创建 PDF 相册

    我有一组 PDF 相册模板 它们有空白方块 用于放置文本和照片 我的需要是使用这些模板来生成实际的专辑 我的计划是使用 iText 和 Java 我会向应用程序发送一个包含所有要使用的图像 URL 的数组 我将确切地知道图像应该放置在模板上
  • 在内存对象缓存中开发

    我正在开发一个基于网络的医疗应用程序 需要创建一个小型内存对象缓存 这是我的用例 我们需要显示需要某些东西 血液 肾脏等 的人提交的请求列表 并且它不会是一个巨大的列表 因为在某一天对血液或其他任何东西的请求将是有限的 请注意 我们不想使用
  • 如何使用 Spring 使用注释执行基于构造函数的依赖注入?

    好的 如果我需要在构造函数中放入一些原始值 我该怎么做 Autowired public CustomBean String name Qualifier SuperBean SuperBean superBean super this s
  • 如何使用 Firebase 查询中的信息填充 Android ListView

    这是我的第一篇文章 所以如果我没有遵循我应该遵循的一些协议 我深表歉意 我正在尝试使用 Firebase 数据库中的一些信息填充 ListView 我认为我遇到的问题是对数据库的查询太慢 线程可能正在下载图片 并且我的活动加载其活动布局而不

随机推荐

  • 合法括号序列判断

    对于一个字符串 xff0c 请设计一个算法 xff0c 判断其是否为一个合法的括号串 给定一个字符串A 和它的长度n xff0c 请返回一个bool值代表它是否为一个合法的括号串 测试样例 xff1a 34 34 6 返回 xff1a tr
  • 最长无重复字符子串

    对于一个字符串 请设计一个高效算法 xff0c 找到字符串的最长无重复字符的子串长度 给定一个字符串A 及它的长度n xff0c 请返回它的最长无重复字符子串长度 保证A中字符全部为小写英文字符 xff0c 且长度小于等于500 测试样例
  • 列出一些你常见的运行时异常(非检查异常)?

    ArithmeticException xff08 算术异常 xff09 ClassCastException xff08 类转换异常 xff09 IllegalArgumentException xff08 非法参数异常 xff09 In
  • 阐述final、finally、finalize的区别

    final xff1a 修饰符 xff08 关键字 xff09 有三种用法 xff1a 如果一个类被声明为final xff0c 意味着它不能再派生出新的子类 xff0c 即不能被继承 xff0c 因此它和abstract是反义词 将变量声
  • 检查是否为BST

    题目描述 请实现一个函数 xff0c 检查一棵二叉树是否为二叉查找树 给定树的根结点指针TreeNode root xff0c 请返回一个bool xff0c 代表该树是否为二叉查找树 代码如下 xff1a package com mian
  • 线程的sleep()方法和yield()方法有什么区别?

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 sleep 方法给其他线程运行机会时不考虑线程的优先级 xff0c 因此会给低优先级的线程以运行的机会 xff1b yield 方
  • 可查询最值的栈

    定义栈的数据结构 xff0c 请在该类型中实现一个能够得到栈最小元素的min函数 代码如下 xff1a 定义两个栈 xff0c 一个stackData xff0c 一个stackMin 将数组中的元素一个个压入stackData栈的时候 x
  • 归并排序(详解)

    时间复杂度 xff1a O xff08 n logn xff09 空间复杂度 xff1a O xff08 n xff09 归并排序分为拆分和归并两个过程 拆分 xff1a 拆分是一个递归的过程 xff0c 实质是将待排序数组均分再均分 xf
  • 双栈队列

    编写一个类 只能用两个栈结构实现队列 支持队列的基本操作 push xff0c pop 给定一个操作序列ope 及它的长度n xff0c 其中元素为正数代表push操作 xff0c 为0代表pop操作 xff0c 保证操作序列合法且一定含p
  • 在进行数据库编程时,连接池有什么作用?

    由于创建连接和释放连接都有很大的开销 xff08 尤其是数据库服务器不在本地时 xff0c 每次建立连接都需要进行TCP 的三次握手 xff0c 释放连接需要进行TCP四次握手 xff0c 造成的开销是不可忽视的 xff09 为了提升系统访
  • Java 虚拟机 gc算法总结

    一 垃圾收集基本的算法 1 引用计数 Reference Counting 为每一个对象添加一个计数器 xff0c 计数器记录了对该对象的活跃引用的数量 如果计数器为0 xff0c 则说明这个对象没有被任何变量所引用 xff0c 即应该进行
  • JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)

    从jason0539转载 链接地址http blog csdn net jason0539 在面向对象编程中 最通常的方法是一个new操作符产生一个对象实例 new操作符就是用来构造对象实例的 但是在一些情况下 new操作符直接生成对象会带
  • JAVA设计模式之抽象工厂模式

    从jason0539转载 链接地址http blog csdn net jason0539 前面已经介绍过 简单工厂模式和工厂方法模式 xff0c 这里继续介绍第三种工厂模式 xff0d 抽象工厂模式 xff0c 还是以汽车的制造为例 例子
  • 双栈排序

    请编写一个程序 xff0c 按升序对栈进行排序 xff08 即最大元素位于栈顶 xff09 xff0c 要求最多只能使用一个额外的栈存放临时数据 xff0c 但不得将元素复制到别的数据结构中 给定一个int numbers C 43 43
  • TCP/IP协议与UDP/IP协议的区别

    TCP xff08 Transmission Control Protocol xff0c 传输控制协议 xff09 是面向连接的协议 xff0c 也就是说 xff0c 在收发数据前 xff0c 必须和对方建立可靠的连接 一个TCP连接必须
  • 滑动窗口

    有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑到最右边 窗口每次向右边滑一个位置 返回一个长度为n w 43 1的数组res xff0c res i 表示每一种窗口状态下的最大值 以数组为 4 3 5 4 3 3 6 7
  • 数组变树

    对于一个没有重复元素的整数数组 xff0c 请用其中元素构造一棵MaxTree xff0c MaxTree定义为一棵二叉树 xff0c 其中的节点与数组元素一一对应 xff0c 同时对于MaxTree的每棵子树 xff0c 它的根的元素值为
  • JAVA设计模式初探之适配器模式

    转载http blog csdn net jason0539 1 概述 将一个类的接口转换成客户希望的另外一个接口 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作 2 解决的问题 即Adapter模式使得原本由
  • Linux使用Note

    这个文档正在维护完善中 1 释放Swap空间 依次执行如下命令即可 xff0c span class token function sync span span class token builtin class name echo spa
  • JAVA设计模式之单例模式

    转载http blog csdn net jason0539 概念 xff1a java 中单例模式是一种常见的设计模式 xff0c 单例模式的写法有好几种 xff0c 这里主要介绍三种 xff1a 懒汉式单例 饿汉式单例 登记式单例 单例