单例模式,利用工厂类只生产一个实例或者有限个实例,比如一个系统中只有一个主板,一个显卡,但是有些系统中也可以存在多个,双显卡,双显示器等等。
这些在Singleton Module中都是可以定义的。当然我们也可以使用全局的变量或者静态类或者对象去做,但是单例模式的存在是有其优点的。
public class SingletonFactory
{
public static SingletonFactory uniqueInstance = null;
private SingletonFactory()
{
}
public static SingletonFactory getInstance()
{
if (uniqueInstance == null)
{
uniqueInstance = new SingletonFactory();
}
return uniqueInstance;
}
}
单例模式的设计范例:
1. 有一个静态的单例模式类型的实例,初始为null
public static SingletonFactory uniqueInstance = null;
2. 注意其构造函数应该为私有的,如果为公有则在外面就可以通过new来创建对象了,否则只能在类的内部调用new SingletonFactory()来创建对象,而这才是我们想要的。
3. 在getInstance函数中做一个判断,就可以让这个类的实例唯一了。
---------------------------------------------------------------------------------------------
但是这样的做法在多线程中会有问题,因为程序是按时间片来运行,如果线程A正在运行
if (uniqueInstance == null)
因为是空,所以执行
uniqueInstance = new SingletonFactory();
但是还没有执行将要执行之时,时间片现在跳转到线程B来执行
if (uniqueInstance == null)
同样会执行
uniqueInstance = new SingletonFactory();
结果就是,因为多线程的原因使其存在了2个实例。。。这并不是我们想要看到的。
那么解决这个问题的办法有多种,各有优缺点。
1. 锁住那个uniqueInstance,比较消耗资源。
public static SingletonFactory getInstance()
{
lock(uniqueInstance)
{
if (uniqueInstance == null)
{
uniqueInstance = new SingletonFactory();
}
}
return uniqueInstance;
}
2. 直接创建一个,但是有可能程序从头到尾都没用到过。
public static SingletonFactory uniqueInstance = new SingletonFactory();
3. 双重检查加锁法,这个是比较推荐的
首先要
public static volatile SingletonFactory uniqueInstance = null;
或许我需要把static和volatile的区别开一个微博说一说,不过虽然static是静态的,虽然在全局只有一个实例,但是对于多线程来说有一个问题,这个实例虽然在A线程中被赋值,但是B线程在使用它的时候并不能完全确认是A线程赋值之前还是A线程赋值之后。于是乎,我们这里用到了volatile,这个关键字的好处是对这个关键字修饰的变量赋值会使所有使用到他的地方的值都发生改变。
其次
public static SingletonFactory getInstance()
{
if (uniqueInstance == null) //这里先判断
{
lock (uniqueInstance) //然后再锁
{
if (uniqueInstance == null) //锁完再判断
{
uniqueInstance = new SingletonFactory();
}
}
}
return uniqueInstance;
}
这样做的好处是,因为我们知道lock是比较消耗资源的,以上的双重检查加锁的话,可以保证lock的操作只执行了一次。
因此我们比较推崇这种写法。