大话单例模式

今天来聊一聊单例模式,单例模式是我们平常使用较多的设计模式之一。刚开始接触单例模式的时候,觉得这个模式比较简单,容易理解,后来发现情况并不是这样。关于单例模式的争论也是比较多的,这些争论基本都是站在一个比较极端的角度来看单例模式,当然,从这些角度来看,单例模式也会有很多的问题,于是,人们开始解决这些问题,解决完他们说,单例模式是邪恶的。接下来我以几个争论点为起点,谈谈我世界里的单例模式。

1.懒汉模式中如何保证线程安全?

先来看看我们经常使用的懒汉模式,如下:

public class Singleton {

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


在执行getInstance方法时可能会有线程安全的问题,当两个线程并行的执行此方法,并且都判断了instance是否为空,这个时候,两个线程都会创建Singleton对象。是的,这里确实会有线程安全的问题,但是,我在开发中还是会经常用到上面的方法来实现单例,因为我可以确保第一次调用时不会有线程安全的问题。比如我经常用单例模式来管理项目的配置信息,在应用启动时,我会调用getInstance方法来获得一些基本的配置信息。记住,线程是否安全是由其使用场景决定的。

为了确保线程安全的问题,于是用上了锁:

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

加了同步以后,安全了,但是同步的开销大了,效率上又不行了。于是,又开始了新一轮优化,其名美曰:双重检测。如下:

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

双重检测的理论是完美的,不幸的是,现实测试是有问题的。再后来,牛逼的人们利用静态类让一切变的完美:

public class Singleton {

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

加载一个类时,其静态内部类不会同时被加载,只有当静态内部类的某个成员被调用时,他才会加载。当然,这个结论我们可以测试一下。

2.单例模式真的能保证单例么?

为了证明单例模式有问题,有人就发明了这么一种特殊情况:

// 1.正常方式获得对象
Singleton instance1 = Singleton.getInstance();
// 2.反射方式活得对象
Class cl = Class.forName("com.test.Singleton");
Constructor[] c = cl.getDeclaredConstructors();
c[0].setAccessible(true);
Singleton instance2 = (Singleton) c[0].newInstance();

单例?你看,我可以搞出”鸳鸯“,借用我高中老师的一句话:鸟大了,什么林子都有。有了反例,争论又开始了。有个人是这么反驳的:你每天单例单例,单例的是什么?单例用在哪里?用单例其实是想保存一些状态,我们只要保证状态是唯一的不就可以了?于是乎就有人用类的静态属性来保持状态,勉强的安慰了自己。用类的静态属性?那您请三思

3.返璞归真

兜了一圈风,回到原点,我还没说最简单的单例模式的实现。把他放在最后,因为他是我们最推荐使用的。简单的就是最美的:

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

	private Singleton() {
	}

	public static Singleton getInstance() {
		return instance;
	}
}

为什么这么说了?懒汉模式为了延迟初始化设计的,如果单例类一直没有被使用,那么他就不会初始化,在这个角度上来说懒汉模式节约资源。但是回过头来想想,如果一直没有使用单例类,那可能是一种设计上的问题,也就是说,很多情况下,懒汉模式根本偷不了懒。所以一般情况下,我们就使用上面这个最简单的代码来实现单例。如果非要搞懒汗,我们上面也讨论了,可以用静态内部类来实现。至于第二个争论,用反射再来new出一个单例,我们姑且忽略,这种情况要么是思路的问题,要么是设计的问题,反正不是单例模式的问题。

转载于:https://my.oschina.net/xishuixixia/blog/94975

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值