单例模式

本文深入解析单例模式的原理及五种实现方式:饿汉式、懒汉式、双重校验锁、静态内部类和枚举。探讨每种方式的特点、优缺点及适用场景。

什么是单例模式

单例模式是确保一个类只有一个对象实例,并提供访问它的全局访问点。

常用于工具类或需要控制实例数量节省资源的场景。

单例模式的实现关键是构造方法私有,并提供一个获取唯一对象的接口。

单例模式有五种实现方式:懒汉式、饿汉式、双重校验锁(DCL)、静态内部类(推荐使用)、枚举。

饿汉式

为啥叫饿汉式的单例模式了?

这是因为饿汉式的单例模式会在类加载时就创建唯一实例,跟个饿汉似的上来就吃。

 public class Singleton {  
      // 唯一实例
      private static Singleton instance = new Singleton();  
      private Singleton (){ }
 ​
      public static Singleton getInstance() {  
          return instance;  
      }  
  }  

懒汉式

那懒汉式为啥又叫懒汉式了?

懒汉式就是懒啊,他得在要使用的时候才会初始化唯一实例。

 //线程不安全的懒汉式
 public class Singleton {  
       // 唯一实例
       private static Singleton instance;  
 ​
       private Singleton (){ }   
 ​
       public static Singleton getInstance() {  
           if (instance == null) {  
               instance = new Singleton();  
           }  
           return instance;  
       }  
 ​
  }  
 ​
 //线程安全的懒汉式
 public class Singleton {  
       private static Singleton instance;  
 ​
       private Singleton (){ }
 ​
       public static synchronized Singleton getInstance() {  
           if (instance == null) {  
               instance = new Singleton();  
           }  
           return instance;  
 ​
       }  
 ​
  }  

 

双重校验锁DCL(重点)

 public class Singleton {  
       //唯一实例
       //使用volatile避免指令重排序问题(重点)
       private volatile static Singleton instance;  
 ​
       private Singleton (){}   
 ​
       public static Singleton getInstance() {  
           //同步会造成性能下降,在同步前通过判读instance是否初始化,减少不必要的同步开销。(重点)
           if (instance== null) {  
               synchronized (Singleton.class) {  
                   //(重点)
                   //如果不在这里加一个为空判断,则可能在第一个为空判断时进来多个对象,然后依次使用同步代码块创建多个对象
                   if (instance== null) {  
                       //延迟初始化
                       instance= new Singleton();  
                   }  
              }  
          }  
 ​
         return singleton;  
      }  
  }  

什么是重排序问题

Java代码到运行会经过三次重排序,是一种优化机制。instance= new Singleton();经过编译后会分为三步1:分配内存空间 2:初始化对象 3:设置instance指向刚排序的内存空间。发生重排序,步骤变为1->3->2。线程A调用getsingleton方法执行到第3步时,线程B调用getInstance方法,在判断instance==null时不为null,则返回instance。但此时instance并还没初始化完毕,线程B访问的将是个还没初始化完毕的对象。

 

静态内部类(推荐使用)

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

怎么做到线程安全的?

JVM在执行类的初始化阶段,会获得一个可以同步多个线程的对同一个类初始化的锁。

枚举

 public enum Sington{
 ​
     INSTANCE;
 ​
     private xxx instance;
 ​
     private Sington(){ }
 ​
     public static xxx getInstance(){
         return instance;
     }
 ​
 }

 

 是否延迟初始化是否多线程安全实现难度优点缺点
饿汉式没有加锁,执行效率会提高类加载时就初始化,浪费内存
懒汉式第一次调用才初始化,避免内存浪费必须加锁 synchronized 才能保证单例,但加锁会影响效率。
双重校验锁较复杂采用双锁机制,安全且在多线程情况下能保持高性能代码复杂
静态内部类一般实现代码简洁、延迟初始化、线程安全相当好啊
枚举能避免多线程同步问题,而且还自动支持序列化机制因为JDK1.5才加入枚举,使用起来感觉陌生
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值