首先说明,此法并非我本人原创,而是转载。原文地址:http://devbean.blog.51cto.com/448512/203501/。我只是说说我受到的启发。
当然了,在此之前,首先解释一下什么是单例模式。
所谓单例模式,就是包含以下特征:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
如果太抽象,没关系,举一个很现实的例子。
Calendar日期类,这个类是不能 Calendar cl=new Calendar()这么写的,IDE会报错,不用IDE写虽然不报错,但是也肯定编译不过去。因为这个类主要是获得系统时间,是一个常量(相对而言)。系统里的时间是只能有一个的(否则一个上午七点一个下午七点就不知道该看哪一个了),符合第一条;Calendar类的正确实例化方式是Calendar cl=Calendar.getInstance(),其中getInstance()是一个静态方法,可以通过函数名直接调用,符合第三条;然后getgetInstance()方法里是这么写的:
- public static Calendar getInstance()
- {
- Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
- cal.sharedZone = true;
- return cal;
- }
复制代码 自己创建了唯一的实例,符合2.因此Calendar类是依循单例模式设计的。
好吧,那么如何实现单例模式呢?
简单的写法是这样:
- package singleton;
- public class Singleton1 {
- //创建一个实例
- private static final Singleton1 s1=new Singleton1();
- //必须是自己创建这个实例,别人无法调用其构造方法
- private Singleton1(){
- }
- //必须自己创建这个实例供其他人调用
- public static Singleton1 getInstance(){
- return s1;
- }
- }
复制代码 但是这个方法有个缺点,就是一旦私有的构造方法里有调用很大资源的东西(例:读取大量数据并进行分析比对),而这个类又被用的比较散(或者干脆没用到),那么这么个设计是很浪费资源的。因为类的实例化是有static修饰,所以不管用不用都的有这么个占内存的玩意儿。
那么大家会说,我一开始置空,需要的时候在实例化。然后一直调用这个实例可以嘛?
- package singleton;
- public class Singleton2 {
- //创建实例,但是首先置空
- private static Singleton2 s2=null;
- //必须是自己创建这个实例,别人无法调用其构造方法
- private Singleton2(){
- }
- //必须自己创建这个实例供其他人调用
- public static Singleton2 getInstance(){
- //运行时进行检测
- //如果是首次加载,则初始化实例
- //如果是以后加载,则调用已实例
- if(s2==null){
- s2=new Singleton2();
- }
- return s2;
- }
- }
复制代码 这种情况,看上去,是对的,实际上,是错的。因为这只是在单线程下可以保证正确运行,如果是多线程。假设A线程调用了此类的实例化方法,由于此前没调用,所以Singleton2 s2是空值,那么A线程就下达了实例化的指令。然后,此时B线程好死不死的冲过来抢了A线程的资源,所以A线程靠边站,B线程来执行实例化操作。此时因为A是处于将实例化还是没有实例化的阶段,所以Singleton2 s2还是空值,所以B线程也会发出进行实例化的指令,完成实例化。然后资源再分配给A,此时Singleton2 s2已经被B实例化过了,实际上已经不是空值。而A因为之前检测过了,没有再回头看一眼的情况下,也会继续按照“Singleton2 s2是空值”这个错误的前提去进行操作,又实例化出一个对象s2,造成了同一系统中有两个实例的坑爹情况。所以要加各种synchronized用来防止抢线程出现。具体过程比较坑,有兴趣的可以研究。 我来解释下高明的做法:
- package singleton;
- public class Singleton5 {
- private Singleton5(){
- System.out.println("==test");
- }
- //调用私有类的静态变量
- public static Singleton5 getInstance(){
- return Singleton5Instance.instance;
- }
- //创建内部私有类
- private static class Singleton5Instance{
- private static final Singleton5 instance=new Singleton5();
- }
- }
复制代码 这种写法高明就高明在,通过增加一个内部类,完美规避了以上提出的2点障碍。
1 占资源。
虽然内部类是用static修饰的,但是外部类是完完全全的原生态。也就是说,不动外部类,内部类是不会参与到系统资源分配的。
2 线程安全
这种写法高明就高明在,完全把线程安全用私有类的构造解决了。换句话说,要实例化Singleton5 ,必先调用getInstance;要调用getInstance方法,必先构造内部类。但是构造方法是完全不可分割的。换句话说不会有线程A构造了一半资源被B抢走的情况。
写到这里我想大呼一声:给大爷跪了!
可能这个例子很小很小,也没什么大不了。但是对我来说,这种闪耀着创新以及智慧的光芒就是我追寻的目标啊!
所以说编程是多么有意的事情,同意的请举手。
|