单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
标准的单例模式:
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton GetInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
描述:通过方法里面的if判断来延时加载单例对象
构造方法让其private,这就堵死了外界利用new创建此类实例的可能
GetInstance方法是获得本类实例的唯一全局访问点
if判断:若实例不存在,则new一个实例,否则返回已有的实例
客户端代码:
public static void main(String[] args) {
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if(s1==s2){
System.out.println("两个对象是相同的实例");
}
}
此种方法存在的问题:
多线程的程序中,多个线程同时访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例的。
解决办法:
可以给线程加一把锁来处理。这里需要解释一下lock语句的含义,lock是确保党一个线程位于代码的临界区时,另一个线程不进入临界区。如果其它线程试图进入锁定的代码,则它将一直等待(即被阻止),直到改对象被释放。
二.多线程时的单例
public class Singleton {
private static Singleton instance;
private static readonly Object syn=new Object();
private Singleton(){}
public static Singleton GetInstance(){
lock(syn){
if(instance==null){
instance=new Singleton();
}
}
return instance;
}
}
描述:在同一个时刻加了锁的那部分程序只有一个线程可以进入。
这段代码使得对象实例由最先进入的那个线程创建,以后的线程再进入时不会再去创建对象实例了,由于有了lock,就保证了多线程环境下的同事访问也不会造成多个实例的生成。
那么为什么不直接lock(instance),而是再创建一个syn对象来lock呢?
因为加锁时,instance实例有没有被创建过实例都还不知道,怎么对它加锁呢。
或者:
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton GetInstance(){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
return instance;
}
}
描述:在同一个时刻加了锁的那部分程序只有一个线程可以进入。
三.双重锁定
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton GetInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
描述:现在这样我们不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为双重锁定。
那么我在外面已经判断了instance实例是否存在,为什么在lock里面还需要做一次instances实例是否存在的判断呢?
因为:对于instance存在的情况,就直接返回,这样没有问题。当instance为null,并且同时有两个线程调用GetInstance()方法时,它们将都可以通过第一重的判断。然后由于lock的机制,这两个线程则只有一个进入,另一个在外排队等候,必须要其中的一个进入并出来后,另一个才能进入。而此时如果没有了第二重的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,这就没有达到单例的目的。