今天来看单例模式,是类结构比较简单的一个设计模式,事实上,他只有一个类。哈哈哈,那我们接下来就看看吧。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
这里面先是定义了一个私有化的本类属性,接着是一个私有化的构造方法,只有本类内才能调用构造方法,下面是重点,通过 getInstance() 方法使得外面的类能获得本类对象,首先是一个判断,如果本类属性 uniqueInstance 为空,则实例化一个属性,否则直接返回这个属性,以保证我们的对象只有一个。
单例模式确保一个类只有一个实例,并提供一个全局访问点。
getInstance() 方法可以延迟我们这个类的实例化,这个 uniqueInstance 类属性是这个类唯一的实例化对象,只有当第一次使用到这个的对象时,也就是第一次调用 getInstance() 方法时才会 uniqueInstance 实例化,以后再用这个类时,直接返回这个属性。
但这段代码也不是无懈可击的,多线程的情况下,使用这段代码简直是灾难,现在我们看看如何解决:
public class SingletonThread {
private static SingletonThread uniqueInstance;
private SingletonThread() {}
public static synchronized SingletonThread getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new SingletonThread();
}
return uniqueInstance;
}
}
就是通过增加 synchronized 关键字到 getInstance() 方法中,迫使每个线程在进入到这个方法之前要等到其他线程离开该方法。但是这样还是有问题,首先是同步会降低性能,其次,我们只有在第一次实例化这个属性时才需要设置同步,一旦设置好 uniqueInstance 就不需要同步了,所以后面的每一次同步都是多余的步骤。
所以我们可以在 JVM 加载这个类时就做好实例化工作,看下面的代码:
public class SingletonFirst {
private static SingletonFirst uniqueInstance = new SingletonFirst();
private SingletonFirst() {}
public static SingletonFirst getInstance() {
return uniqueInstance;
}
}
这样我们在 JVM 加载这个类时,就可以马上创建唯一的单件实例。JVM 保证任何线程访问这个静态变量之前都会先创建这个实例。
还有一种方法,使用「双重检查锁」,在 getInstance() 减少使用同步,代码如下:
public class SingletonDouble {
private volatile static SingletonDouble uniqueInstance;
private SingletonDouble() {}
public static SingletonDouble getInstance() {
if (uniqueInstance == null) {
synchronized (SingletonDouble.class) {
if (uniqueInstance == null) {
uniqueInstance = new SingletonDouble();
}
}
}
return uniqueInstance;
}
}
这个方法,我们可以大大减少时间消耗,首先检查实例,如果没有,进入同步区块,再一次检查实例,如果没有才创建,注意,同步区块这段代码,我们只会执行一次,只有第一次才会同步。双重检查锁机制不适用于 1.4 及更早的版本。
好了,所有单例模式的实现方式就都写完,好好记得吧。