设计模式学习(五)——单例模式

参考书——《HeadFirst设计模式》


首先,来个苏格拉底式的分析,如下:

一个公共类,如果它的构造器是private的,会怎样?

public class Singleton {
	private Singleton(){}
}
含有私有构造器的类不能被实例化。


那有可以使用私有构造器的对象么?

该类内的代码估计是唯一能使用这个私有构造器的代码。

为什么?

因为必须有Singleton的实例才能够调用它的构造器,但是又没有其他类能够实例化Singleton。有点像“鸡生蛋,蛋生鸡”的问题。


但是如果加一个这样的方法呢?public static Singleton getInstance()

这个方法不需要用对象调用,可以通过类名.方法名调用。


再进一步,如果这样写呢?是否就可以初始化一个Singleton呢?

public class Singleton {
	private Singleton(){}
	
	public static Singleton getInstance(){
		return new Singleton();
	}
}
当然可以


剖析经典单例模式

public class Singleton {
    private static Singleton uniqueInstance;  //利用一个静态变量来记录Singleton类的唯一实例
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if(uniqueInstance==null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

====================================================================================================================================

多线程与单例(写到这里,不得不说《HeadFirst设计模式》真的是本很好的书!!!建议以java为语言的人,学习设计模式的时候根本就没有必要看其他的设计模式书,这本讲的最好,当然个人意见。)

上面的经典单例模式就是俗称的“懒汉式”,只有在需要的时候才创建这个实例,这就是“延迟实例化(Lazy instantiaze)”。

遇到多线程怎么办?首先会想到加关键字synchronized到getInstance() 方法,不错!但是,你必须知道同步一个方法可能造成程序执行效率大幅度的下降。那,有没有其他的办法?

“饿汉式”的提出

public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
	
	private Singleton() {}
	
	public static Singleton getInstance() {
		return uniqueInstance;
	}
}

利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单例实例。但是,对于资源密集,配置开销较大的单例这样的方式就不是很好。


接下来,采用“”双重检查加锁“的单例模式(只需要在需要创建一个实例的时候才进行同步,所以实际上只有在第一次调用getInstance的时候才会出现同步)

代码:

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

为什么要进行两次判断?

因为实例为null时多个线程可以并发的进入if内部,然后一个线程进入synchronized块来创建实例,其他线程被阻断。当第一个线程退出synchronized块后,另一个线程就会进入,如果不加第二次判断就会创建多个实例。


关键字 volatile

属性instance是被volatile修饰的。volatile具有synchronized的可见性特点,换而言之,线程能够自动发现volatile变量的最新值。这样,如果instance实例化成功,其他线程便能立即发现。


最后再说一下 类加载器与单例

每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次,如果这样的事情发生在单例上,就会产生多个单例并存的怪异现象。解决办法:自行制定类加载器,并制定同一类加载器。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值