Singleton模式

今天在看了设计模式解析之后对singleton又有了新的认识, 在此记一下哈

一个最简单的Singleton实现:

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

}

考虑了并发的Singleton实现:

public class DoubleCheckedSingleton {
	
	private static DoubleCheckedSingleton instance;
	
	private DoubleCheckedSingleton(){}
	
	//方法级别的synchronized会成为性能瓶颈
	public static DoubleCheckedSingleton getInstance(){
		//可以将synchronized关键字放在这里,但是调用者每次getInstance时都会
		//在==null的判断前等待,造成性能瓶颈.可以在==null外面再套一层,这样只会
		//在实例化之前阻塞程序
		if(instance == null){
			synchronized(DoubleCheckedSingleton.class){
				if(instance == null){
					instance = new DoubleCheckedSingleton();
				}
			}
		}
		return instance;
		
	}
}

这样的代码看上去似乎没什么问题, 但是存在指令重排, Object o = new Object 可分解为三个操作:1. 申请一片内存空间, 2.调用Object的构造方法, 3.将new的对象的引用赋值给o.编译器出于优化程序的目的, 会适当调整123的顺序, 比如会变成132. 在3和2之间,此时假设另一个线程进入了getInstance方法,判断得到的instance不为null, 于是乎就把这个引用拿走使用了, 但是其实这个引用并不是一个有效的引用, 因为他还没构造完成.  因此这样的方式还是无法解决同步的问题. jdk1.5之后, 加强了volatile关键字的语义, 使用volatile关键字就可以禁止指令重排问题了, 即吧instance声明为volatile.

    volatile的两个语义:一是对volatile变量的写操作happen-before对同一变量的读操作, 二是屏蔽指令重排序.

不过书中提出了一只利用java类加载器的机制实现了只初始化一次

public class InnerClassSingleton {
	
	private InnerClassSingleton(){}
	
	private static class Instance{
		static final InnerClassSingleton instance = new InnerClassSingleton();
	}
	
	public static InnerClassSingleton getInstance(){
		return Instance.instance;
	}

}
即Instance类只会被加载一次(member class的加载时机为使用时加载,见http://stackoverflow.com/questions/24538509/does-the-java-classloader-load-inner-classes),所以只会创建一个对象.JVM能够保证当一个类被加载的时候, 这个类的加载过程是线程互斥的。因此当我们在第一次调用getInstance方法的时候,JVM能够确保instance只被创建一次。


如果不需要懒加载的话, 其实可以不用内部类

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

}

作为静态成员变量的instance应该照样只会加载一次吧, 相比于第一种Singleton的实现, 无非就多了一块内存的申请罢了,

---------------------华丽的分割线-------------------

上述程序确实可以很好的实现并发中的单例, 但是在第一次加载类的时候就创建了MySingleton,并不适用于惰性加载的情况。而使用内部类实现的话,由于在jvm中“当一个类被加载时,其内部类(包括静态内部类)是不会同时被加载的,只有在使用到的时候,内部类才会被加载”,因此很好的实现了惰性加载。


事后看到一篇文章, 自认为是看到最好的解释, 收藏下

http://www.blogjava.net/zellux/archive/2008/04/07/191365.html

http://www.iteye.com/topic/260515这篇文章使用happen-before来解释了DCL的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值