【单例模式】

打印 上一主题 下一主题

主题 700|帖子 700|积分 2100

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。


一、实现方式


1. 饿汉式

 在类加载的时候就创建实例,无论是否使用,实例都会被创建。优点是实现简单,线程安全。缺点是大概造成资源浪费,而步伐大概不肯定会使用这个实例。

代码示例:
  1. public class Singleton {
  2.     //创建实例,并且用static保证实例唯一
  3.     private static final Singleton instance = new Singleton();
  4.    
  5.     //把构造方法设置为private,在类外就无法通过new的方式来创建Singleton实例
  6.     private Singleton() {}
  7.    
  8.     //获取实例
  9.     public static Singleton getInstance() {
  10.         return instance;
  11.     }
  12. }
复制代码



2. 懒汉式写法


  1. public class Singleton {
  2.     private static Singleton instance = null;
  3.     private Singleton() {}
  4.     public static synchronized Singleton getInstance() {
  5.         if (instance == null) {
  6.             instance = new Singleton();
  7.         }
  8.         return instance;
  9.     }
  10. }
复制代码
阐明:在第一次调用获取实例方法时才创建实例。

思考:上述写的饿汉模式懒汉模式,在多线程环境中是否安全呢?为什么?

分析饿汉模式:

饿汉模式,只涉及到读操作,因此,在多线程环境中是线程安全的。

分析懒汉模式:

懒汉模式,涉及到了读和写操作,会出现脏读的情况,因此,懒汉模式不是线程安全的。 

如图所示:



通过加锁改进刚才代码:
  1. public class Singleton {
  2.     private static Singleton instance = null;
  3.     private Singleton() {
  4.     }
  5.     public static Singleton getInstance() {
  6.         synchronized (Singleton.class) {
  7.             if (instance == null) {
  8.                 instance = new Singleton();
  9.             }
  10.         }
  11.         return instance;
  12.     }
  13. }
复制代码

阐明如下图所示:

刚才代码还有缺陷:分析代码:
  1. public static Singleton getInstance() {
  2.         synchronized (Singleton.class) {
  3.             if (instance == null) {
  4.                 instance = new Singleton();
  5.             }
  6.         }
  7.         return instance;
  8.     }
复制代码
思考: 
如果每次调用getInstance方法的时候,都会进行加锁操作,加锁操作是有性能开销的,真的需要每次都进行加锁吗?

分析:这里的加锁在new出对象之前加上是有必要的,但是,一旦对象new完了,后续调用getInstance方法,此时instance的值肯定好坏空的,就会触发return。
解决方案:加上一个条件判断,如果对象没有创建才加锁,否则就return。

代码示例:
  1. public class Singleton {
  2.     private static volatile Singleton instance;
  3.     private Singleton() {}
  4.     public static Singleton getInstance() {
  5.         //这个条件判断的作用是:是否要加锁
  6.         if (instance == null) {  
  7.             synchronized (Singleton.class) {
  8.                
  9.                 //这个条件判断的作用是:是否要创建对象
  10.                 if (instance == null) {
  11.                     instance = new Singleton();
  12.                 }
  13.             }
  14.         }
  15.         return instance;
  16.     }
  17. }
复制代码

你以为就完了吗?
上述代码当中还存在内存可见性和指令重排序的题目。
分析:假设现在有很多线程去调用getInstance方法,只有第一次读的时候才去读内存,后续都是读寄存器或者缓存(此时有如果发生了写操作,其他线程,或者处置处罚器是感知不到的)就会发生内存可见性题目。

指令重排序题目本质上就是编译器或者处置处罚器优化。

instance = new Singleton();这条语句拆分成三个步骤:
   1:申请内存空间
  2:调用构造方法,把这个内存空间初始化成一个公道的对象
  3: 把内存空间的地点赋值给instance引用
   在正常情况下是按照 1 2 3这个顺序去执行,此时此刻,编译器为了进步步伐的执行效率,有大概把这个1 2 3 的执行顺序优化成 1 3 2或者其他情况。
如图所示:



 volatile来解决内存可见性和指令重排序题目:
代码示例:
  1. public class Singleton {
  2.    
  3.     //volatile来解决内存可见性和指令重排序问题
  4.     //static实例唯一,共享
  5.     private static volatile Singleton instance;
  6.    
  7.     //禁止外部new对象
  8.     private Singleton() {}
  9.     public static Singleton getInstance() {
  10.         //这个条件判断的作用是:是否要加锁
  11.         if (instance == null) {  
  12.             synchronized (Singleton.class) {
  13.                
  14.                 //这个条件判断的作用是:是否要创建对象
  15.                 if (instance == null) {
  16.                     instance = new Singleton();
  17.                 }
  18.             }
  19.         }
  20.         return instance;
  21.     }
  22. }
复制代码


3. 静态内部类方式


利用类加载机制实现延长加载,只有在调用获取实例的方法时,才会加载静态内部类,从而创建实例。线程安全,并且实现简单高效。
  1. public class Singleton {
  2.     private Singleton() {}
  3.     private static class SingletonHolder {
  4.         private static final Singleton INSTANCE = new Singleton();
  5.     }
  6.     public static Singleton getInstance() {
  7.         return SingletonHolder.INSTANCE;
  8.     }
  9. }
复制代码

4. 罗列方式

简洁且线程安全。
  1. public enum SingletonEnum {
  2.     INSTANCE;
  3.     public void doSomething() {
  4.         // 具体方法实现
  5.     }
  6. }
  7.  
复制代码

二、单例模式的优点


   1. 减少系统资源开销:制止了频繁创建和烧毁对象带来的资源浪费。
  2. 包管对象的唯一性:确保在整个应用步伐中只有一个实例存在,方便对这个实例进行统一的管理和控制。
  3. 方便资源访问:提供了一个全局访问点,可以方便地获取这个唯一的实例,而不需要在多个地方转达实例对象。
  

三、单例模式的使用场景


1. 日记记录器:通常整个应用步伐只需要一个日记记录器实例,以确保所有的日记信息都被记录到同一个地方。
2. 数据库毗连池:管理数据库毗连,制止频繁地创建和烧毁毗连,进步性能。
3. 设置文件读取:应用步伐通常只需要一个设置文件读取实例,以确保设置信息的一致性。



 


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表