引用《大话设计模式》第二十一章
1. 问题
有一个响应桌面事件的类,里面有对点击按钮事件响应的函数box_Click(),每点击一次按钮,就调用box_Click(),出现一个box。
但如下代码每点击一次按钮,就出现一个新的box,我们想要第一次点击出现新的box,但后面点击都只有那一个box。
//出现问题的代码,这里只贴出了类中响应点击按钮的函数box_Click()
private void box_Click()
{
Box box=new Box();
box.show();
}
-
解决方法一:在响应函数box_Click()判断是否有生成box
private Box box;//声明一个private变量,控制每次点击都只有这一个box private void box_Click() { if(box==null)//第一次点击 { box=new Box(); box.show(); } }
问题:如果有多个地方会用到box,那么每个地方都要判断,代码臃肿且容易出错。是否之前有一个box,应该是交给Box类自己来判断。所以我们修改Box类:
- 解决方法二:这种方法就是单例模式
public class Box
{
private static Box box=null;//储存一个box,每次点击都调用它
public static show();
private Box()//私有构造器
{
...
}
public static Box GetInstance()//外部想要一个box时,调用这个方法
{
if(box==null)//第一次点击
{
box=new Box();//调用私有构造器
}
return box;
}
}
现在的响应函数:
private void box_Click()
{
Box.GetInsance().show();//GetInstance()是static,可以直接调用,返回的是唯一的那个box
}
2. 单例/单件模式
2.1 定义
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。(即上面的GetInstance() )
2.2 结构图和代码
Singleton()是私有的构造器,类似上面的private Box();
GetInstance()是外部访问唯一对象的方法。
-
Singleton类
class Singleton { private static Singleton instance=null; private Singleton(){}; public static Singleton GetInstance() { if(instance==null) { instance=new Singleton; } return instance; } }
-
客户端代码
Singleton s1=Singleton.GetInstance(); Singleton s2=Singleton.GetInstance(); //s1==s2是True
2.3 多线程
在Singleton中instance没有被实例化时,如果有两个线程同时调用GetInstance(),在单处理机下一次只能执行一个线程,有可能发生错误:
线程1判断instance==null是True,还未来得及new;
线程2判断instance==null也是True,然后进行new;
线程1继续执行new,这样就new了两个实例
这时可以加锁,下面是双重锁的代码:
class Singleton
{
private static Singleton instance;
private Object obj=new Object();//用于锁
private Singleon();
public static Singleton GetInstance()
{
if(instance==null)//第一次判定
{
lock(obj)
{
if(instance==null)//第二次判定
{
instance =new Singleton();
}
}
}
return instance;
}
}
-
双重锁定的好处:
不加双重锁:
lock(obj)//没有外层的判定,每个线程都会在这阻塞 { if(instance==null)//只有一次判定 { instance =new Singleton(); } }
如果没有第一次判定,那么不管instance是不是null,每次都会等待,只有一个线程在运行,然后判断。加了第一次判定后,只有第一次同时访问instance的线程会阻塞在锁上,而后面访问的线程都可以直接return了。双重锁定很大的提高了效率。