时间会让人成长,但是不会指明方向
剖析经典的单件模式
public class Singleton {
// 利用一个静态变量来记录Singleton类的唯一实例
private static Singleton uniqueInstance;
// 私有化类构造器
private Singleton() {}
// 获取类单件实例
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
别人想要获取这个类的实例,就必须“请求”得到一个实例,而不是自行实例化得到。类中有一个静态方法,叫做getInstance()
。调用这个方法,单件实例就现身了,这个单件可能是在这次调用的时候被创建出来的,也可能是以前早就被创建出来的。
巧克力工厂
现代化的巧克力工厂具备计算机控制的巧克力锅炉,锅炉做的事,就是把巧克力和牛奶融合在一起,然后在下一个阶段,制造成巧克力。
下面是一个巧克力锅炉制造器的代码,你会发现代码写得非常小心,他们在努力防止不好的事情发生,因为一个不小心就会造成事故。例如:排出500加仑的未煮沸的混合物,或者锅炉已经加满了还继续放原料,或者锅炉内好没放原料就开始空烧。
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
public ChocloateBoiler() {
// 代码开始时,锅炉是空的,未煮沸的
empty = true;
boiled = false;
}
public void fill() {
// 在锅炉内填充原料时,锅炉必须是空的
if (isEmpty()) {
empty = false;
boiled = false;
// 在锅炉中填满巧克力和牛奶的混合物
}
}
public void drain() {
// 锅炉排出时,必须是满的且是煮沸的
if (!isEmpty() && isBoiled()) {
// 排出煮沸的巧克力和牛奶
empty = true;
}
}
public void boil() {
// 煮混合物时,锅炉必须是满的,且没有煮过的
if (!isEmpty() && !isBoiled()) {
// 将锅炉内物煮沸
boiled = true;
}
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
}
如果只存在一个锅炉类的实例,制作流程可以有条不紊地进行下去。但是如果同时存在多个锅炉实例呢,那就可能会发生很糟糕的事情。
利用单件模式改进巧克力工厂
将构造函数私有化,且写一个获取单例的getInstance()
方法。
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
public static ChocolateBoiler getInstance() {
private ChocloateBoiler() {
// 代码开始时,锅炉是空的,未煮沸的
empty = true;
boiled = false;
}
}
public void fill() {
// 在锅炉内填充原料时,锅炉必须是空的
if (isEmpty()) {
empty = false;
boiled = false;
// 在锅炉中填满巧克力和牛奶的混合物
}
}
public void drain() {
// 锅炉排出时,必须是满的且是煮沸的
if (!isEmpty() && isBoiled()) {
// 排出煮沸的巧克力和牛奶
empty = true;
}
}
public void boil() {
// 煮混合物时,锅炉必须是满的,且没有煮过的
if (!isEmpty() && !isBoiled()) {
// 将锅炉内物煮沸
boiled = true;
}
}
public boolean isEmpty() {
return empty;
}
public boolean isBoiled() {
return boiled;
}
}
定义单件模式
确保一个类只有一个实例,并且提供一个全局访问点。
-
uniqueInstance
类变量持有唯一的单件实例. -
getInstance()
方法是静态的,这意味着它是一个类方法,所以可以在代码的任何地方使用Singleton.getInstance()
访问它。这和访问全局变量一样简单,只是多了一个优点,单件可以延迟实例化。
多线程的麻烦
尽管我们使用了经典的单件模式来修改代码,但是在实际的运行过程中还是出现了问题,在锅炉加热的过程中,机子还在不停地加入原料。
原因:多线程的调用。
ChocolateBoiler boiler = ChocolateBoiler.getInstance();
// 多线程出现
fill();
boil();
drain();
这样就导致了上面糟糕情况的发生。
解决多线程带来的问题
-
同步
getInstance()
方法
通过加锁的方式,保证不会有两个线程可以同时进入这个方法。
public class Singleton {
// 利用一个静态变量来记录Singleton类的唯一实例
private static Singleton uniqueInstance;
// 私有化类构造器
private Singleton() {}
// 获取类单件实例
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
弊端:加锁同步会影响性能。
如果你的程序对这点性能不是很影响的话,可以考虑使用。
-
急切实例化(类初始化就创建)
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
// 私有化类构造器
private Singleton() {}
// 获取类单件实例
public static Singleton getInstance() {
return uniqueInstance;
}
}
利用这个做法,我们依赖JVM
在这个类加载时就创建唯一的单件实例。
弊端:如果这个类比较占用系统资源,就会造成资源的浪费。
-
双重检查锁
首先先检查实例是否创建,如果没有创建才进行同步,已经创建了,就不进行同步操作。
public class Singleton {标签:Singleton,锅炉,单件,模式,uniqueInstance,实例,public From: https://www.cnblogs.com/l12138h/p/16986000.html
private volatile static Singleton uniqueInstance = new Singleton();
// 私有化类构造器
private Singleton() {}
// 获取类单件实例
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}