ava单例模式的正确写法

一、懒汉式(线程不安全)

 

Java代码   收藏代码
  1. public class Singleton {  
  2.     private static Singleton instance;  
  3.     private Singleton (){}  
  4.   
  5.     public static Singleton getInstance() {  
  6.      if (instance == null) {  
  7.          instance = new Singleton();  
  8.      }  
  9.      return instance;  
  10.     }  
  11. }  

 

介绍:线程不安全,在多线程情况下容易创建多个实例。

 

 

二、懒汉式(线程安全)

 

Java代码   收藏代码
  1. public static synchronized Singleton getInstance() {  
  2.     if (instance == null) {  
  3.         instance = new Singleton();  
  4.     }  
  5.     return instance;  
  6. }  

  

介绍:虽然线程安全,但是不够高效。

 

 

三、双重检验锁

 

Java代码   收藏代码
  1. public static Singleton getSingleton() {  
  2.     if (instance == null) {                         //Single Checked  
  3.         synchronized (Singleton.class) {  
  4.             if (instance == null) {                 //Double Checked  
  5.                 instance = new Singleton();  
  6.             }  
  7.         }  
  8.     }  
  9.     return instance ;  
  10. }  

 

介绍双重检验锁机制比懒汉式(线程安全)要高效很多,因为它不用对instance不为空的情况进行同步。这段代码看起来似乎很完美,但很可惜,还是存在问题。instance=new Singleton() 这并非是一个原子性质的操作,事实上在JVM中做了如下三件事:

 

  1. 给instance分配内存
  2. 调用构造函数初始化成员变量
  3. 将instance对象指向分配的内存(此步骤完成instance即为非空)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

 

四、双重检验锁(volatile)

 

Java代码   收藏代码
  1. public class Singleton {  
  2.     private volatile static Singleton instance; //声明成 volatile  
  3.     private Singleton (){}  
  4.   
  5.     public static Singleton getSingleton() {  
  6.         if (instance == null) {                           
  7.             synchronized (Singleton.class) {  
  8.                 if (instance == null) {         
  9.                     instance = new Singleton();  
  10.                 }  
  11.             }  
  12.         }  
  13.         return instance;  
  14.     }  
  15.      
  16. }  

 

 介绍:使用 volatile 的原因是可以保证线程在本地不会存有 instance 的副本,每次都是去主内存中读取。这里使用volatile的一个主要原因就是volatile可以禁止JVM指令重排序。

 

五、饿汉式

 

Java代码   收藏代码
  1. public class Singleton{  
  2.     //类加载时就初始化  
  3.     private static final Singleton instance = new Singleton();  
  4.       
  5.     private Singleton(){}  
  6.   
  7.     public static Singleton getInstance(){  
  8.         return instance;  
  9.     }  
  10. }  

 

 介绍:这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。缺点是它不是一种懒加载模式。比如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。

 

五、静态内部类

 

Java代码   收藏代码
  1. public class Singleton {    
  2.     private static class SingletonHolder {    
  3.         private static final Singleton INSTANCE = new Singleton();    
  4.     }    
  5.     private Singleton (){}    
  6.     public static final Singleton getInstance() {    
  7.         return SingletonHolder.INSTANCE;   
  8.     }    
  9. }  

 

 介绍:这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

 

六、枚举

 

Java代码   收藏代码
  1. public enum EasySingleton {  
  2.       
  3.     INSTANCE;  
  4.       
  5.     EasySingleton(){  
  6.           
  7.     }  
  8.       
  9. }  

 

介绍:我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值