设计模式(二)单例模式的七种写法

相关文章
设计模式系列

面试的时候,问到许多年轻的Android开发他所会的设计模式是什么,基本上都会提到单例模式,但是对单例模式也是一知半解,在Android开发中我们经常会运用单例模式,所以我们还是要更了解单例模式才对。

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式结构图:

单例模式有多种写法各有利弊,现在我们来看看各种模式写法。

1. 饿汉模式

public class Singleton {  
     private static Singleton instance = new Singleton();  
     private Singleton (){
     }
     public static Singleton getInstance() {  
     return instance;  
     }  
 }  

这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。 这种方式基于类加载机制避免了多线程的同步问题,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。

2. 懒汉模式(线程不安全)

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

懒汉模式申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且在多线程不能正常工作。

3. 懒汉模式(线程安全)

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

这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。

4. 双重检查模式 (DCL)

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

这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。在这里用到了volatile关键字,不了解volatile关键字的可以查看Java多线程(三)volatile域这篇文章,在这篇文章我也提到了双重检查模式是正确使用volatile关键字的场景之一。
在这里使用volatile会或多或少的影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。 DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL。

5. 静态内部类单例模式

public class Singleton { 
    private Singleton(){
    }
      public static Singleton getInstance(){  
        return SingletonHolder.sInstance;  
    }  
    private static class SingletonHolder {  
        private static final Singleton sInstance = new Singleton();  
    }  
} 

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化sInstance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。

6. 枚举单例

public enum Singleton {  
     INSTANCE;  
     public void doSomeThing() {  
     }  
 }  

  默认枚举实例的创建是线程安全的,并且在任何情况下都是单例,上述讲的几种单例模式实现中,有一种情况下他们会重新创建对象,那就是反序列化,将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。在上述的几个方法示例中如果要杜绝单例对象被反序列化是重新生成对象,就必须加入如下方法:

private Object readResolve() throws ObjectStreamException{
return singleton;
}

枚举单例的优点就是简单,但是大部分应用开发很少用枚举,可读性并不是很高,不建议用。

7. 使用容器实现单例模式

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

用SingletonManager 将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

总结

到这里七中写法都介绍完了,至于选择用哪种形式的单例模式,取决于你的项目本身,是否是有复杂的并发环境,还是需要控制单例对象的资源消耗。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/411726.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【任何电机可使用的七段式s曲线-----包括matlab代码和simulink仿真】

永磁同步电机七段式s曲线 一、前言二、理论分析三、7段式s曲线加减速关系图四、matlab代码代码结果 五、simulink七段式s曲线整体框图simulink结果s-function内嵌代码 六、使用双闭环FOC控制-----simulink仿真示意 一、前言 S形&#xff1a;加加速(T1)→>匀加速(T2)→减加速…

【JVM】StringTable 字符串常量池

参考&#xff1a;javaGuide 字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串&#xff08;String 类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建 String的不可变性 1.通过字面量的方式&#xff08;区别于new&#xff09;给一个…

Avalonia学习(二十六)-桌面系统界面Ribbon

这个界面是开源项目中拔下来的&#xff0c;我没有全部改完&#xff0c;只能按照我得界面测试。我还有一个bug没有找到&#xff0c;但是解决了一下。这里没有任何和大家说的&#xff0c;给大家看一下界面效果。 另外地图研究了缩放和显示鼠标位置经纬度

https://htmlunit.sourceforge.io/

https://htmlunit.sourceforge.io/ 爬虫 HtmlUnit – Welcome to HtmlUnit HtmlUnit 3.11.0 API https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit/2.70.0 https://s01.oss.sonatype.org/service/local/repositories/releases/content/org/htmlunit…

Java核心知识点常考面试题(持续更新中)

Java核心知识点常考面试题&#xff08;持续更新中&#xff09; 线程与线程池线程线程池 Java锁机制java线程模型java锁分类轻量级锁重量级锁ReentrantLock 底层原理与源码深度解析ReentrantReadWriteLock 深入理解读写锁CountDownLatchsemaphore 实现公平锁与非公平锁线程死锁与…

Android 9.0 禁用插入耳机时弹出的保护听力对话框

1.前言 在9.0的系统rom定制化开发中,在某些产品中会对耳机音量调节过高限制,在调高到最大音量的70%的时候,会弹出音量过高弹出警告,所以产品 开发的需要要求去掉这个音量弹窗警告功能,接下来就来具体实现这个功能 2.禁用插入耳机时弹出的保护听力对话框的核心类 framework…

第六十八天 APP攻防-XposedFridaHook证书校验反代理代理转发

第68天 APP攻防-Xposed&Frida&Hook&证书校验&反代理&代理转发 知识点&#xff1a; 1、APP防代理绕过-应用&转发 2、APP证书校验类型-单向&双向 3、APP证书校验绕过-Frida&XP框架等 章节点&#xff1a; 1、信息收集-应用&资产提取&权…

蓝桥杯-标题统计

知识点: 关键是考察getline的作用 #include <iostream> using namespace std; int main() { string a; int t0; getline(cin,a);//每次读取一整行并把Enter键生成的换行符抛弃 for(int i0;i<a.length();i){ if(a[i]! )t; } cout<<t; return …

【LTSPICE】宏模型中的语法分析(持续更新)

本篇文章用来总结模型文件、仿真文件中的语法&#xff0c;写给自己看的&#xff0c;格式和内容上比较随意 上图是在安森美官网上下载的一款二极管的spice模型文件。 * 字符串&#xff1a;注释&#xff0c;能看到这篇文章的应该都懂啥叫注释.model&#xff1a;.一个词是命令…

如何修改图片尺寸大小不变形?简单的图片改大小的方法

在平时工作或者学习时&#xff0c;有时候需要将图片的大小进行修改&#xff0c;以便于存储、分享或打印&#xff0c;很多人都习惯性的去下载一些图片处理软件&#xff0c;比较麻烦&#xff0c;这里推荐大家使用图片在线处理工具&#xff0c;打开浏览器直接将图片尺寸修改&#…

【Flink】Flink 中的时间和窗口之窗口(Window)

1. 窗口的概念 Flink是一种流式计算引擎&#xff0c;主要是来处理无界数据流&#xff0c;数据流的数据是一直都有的&#xff0c;等待流结束输入数据获取所有的流数据在做聚合计算是不可能的。为了更方便高效的处理无界流&#xff0c;一种方式就是把无限的流数据切割成有限的数…

【析】装卸一体化车辆路径问题的自适应并行遗传算法

0 引言 国内外有关 &#xff36;&#xff32;&#xff30;&#xff33;&#xff30;&#xff24;的文献较多&#xff0c;求解目标多以最小化车辆行驶距离为主&#xff0c;但现实中可能存在由租赁费用产生的单次派出成本&#xff0c;需要综合考 虑单次派车成本和配送路径成本。…

消息中间件之RocketMQ源码分析(十八)

Broker CommitLog索引机制中的构建过程 1.创建ConsumeQueue和IndexFile。 ConsumeQueue和IndexFile两个索引都是由ReputMessageService类创建的 RequestMessageService类图 ReputMessageService服务启动后的执行过程。 doReput()方法用于创建索引的入口&#xff0c;通常通过…

Redis 管道详解

Redis 管道 关键词&#xff1a;Pipeline Pipeline 简介 Redis 是一种基于 C/S 模型以及请求/响应协议的 TCP 服务。通常情况下&#xff0c;一个 Redis 命令的请求、响应遵循以下步骤&#xff1a; 客户端向服务端发送一个查询请求&#xff0c;并监听 Socket 返回&#xff08…

3 easy 26. 删除有序数组中的重复项

双指针&#xff1a; //给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 //一致 。然后返回 nums 中唯一元素的个数。 // // 考虑 nums 的唯…

PreMaint CMS系统:数字化驱动起重机远程管理的智能未来

在港口起重机领域&#xff0c;数字化解决方案正成为提高效率、降低停机时间的关键。PreMaint CMS作为起重机核心可视化系统&#xff0c;融合了操作人员、维护团队和管理者的需求&#xff0c;通过智能化的方式&#xff0c;将复杂的数据转化为简洁的信息&#xff0c;实现对起重机…

Node.js中的错误处理和日志记录

在Node.js应用程序中&#xff0c;错误处理和日志记录是非常重要的方面。正确的错误处理和日志记录可以帮助我们更好地跟踪问题、排查 bug&#xff0c;并提供更好的用户体验。在本篇博客中&#xff0c;我将为大家介绍在Node.js中如何进行错误处理和日志记录&#xff0c;以及一些…

BIO实战、NIO编程与直接内存、零拷贝深入辨析

BIO实战、NIO编程与直接内存、零拷贝深入辨析 长连接、短连接 长连接 socket连接后不管是否使用都会保持连接状态多用于操作频繁&#xff0c;点对点的通讯&#xff0c;避免频繁socket创建造成资源浪费&#xff0c;比如TCP 短连接 socket连接后发送完数据后就断开早期的http服…

基于JSP的毕业设计选题系统的设计与实现

基于JSP的毕业设计选题系统的设计与实现 (源代码论文) A. 项目简介 毕业设计选题系统就是能够使学生通过互联网完成毕业设计课题的选定&#xff0c;它采用Web方式&#xff0c;同时适用于局域网和Internet&#xff0c;它要实现审核&#xff0c;权限管理&#xff0c;邮件通知…

Spring6学习技术|IoC|基于注解管理bean

学习材料 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09; IoC注解 首先这是最常用的方法。&#xff08;在学Java基础的时候明明是非常不起眼的知识点啊&#xff01;&#xff01;&#xff01;&#xff09; 从 Java…