设计模式之单例模式
简介
单例模式(Singleton Pattern),是一种常用的软件设计模式。单例模式能够确保一个类在任何时候只有一个实例,并且由类自己管理这个单独的实例,避免其它类产生实例,如果需要访问这个实例,可以通过类提供的全局访问点获取。
这种类型的设计模式属于创建型模式(提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活)。
非线程安全单例模式
public class NonThreadSafe {
private static NonThreadSafe instance;
private NonThreadSafe() {}
public static NonThreadSafe getInstance() {
if (instance == null) {
instance = new NonThreadSafe();
}
return instance;
}
}
-
创建一个private static私有静态类变量instance:保证实例的唯一性,且不会被外部其他类直接调用进行操作。
-
将类的构造方法修饰为private:表示不会被外部其他类调用进而随意创建实例,保证创建实例在NonThreadSafe类中进行。
-
public static的getInstance方法:提供外部类调用进而获取实例。先判断实例是否为null,为null则创建实例并返回;反之,则直接返回实例对象。
在这种实现方式中,我们在需要用到这个实例的时候才会去创建一个实例,如果我们永远用不到这个实例,那么它就永远不会产生,这就是延迟实例化。
但是,上面的代码仅限于单线程场景,若存在多线程访问的情况,很可能会出现多个实例的情况。
线程安全但低效率单例模式
public class ThreadSafePoorEfficiency {
private static ThreadSafePoorEfficiency instance;
private ThreadSafePoorEfficiency() {}
public static synchronized ThreadSafePoorEfficiency getInstance() {
if (instance == null) {
instance = new ThreadSafePoorEfficiency();
}
return instance;
}
}
- 将getInstance()方法用synchronized关键字修饰:使得每个线程在进入这个方法之前必须等待别的线程离开这个方法,这样就保证了不可能存在两个线程同时进入此方法的情况。
该方法属于延迟实例化的实现方式,由于创建实例对象的操作在整个应用中只会在第一次执行,之后,调用getInstance()方法时,只是单纯的返回实例对象而已,即:同步的需求只需要在第一次执行创建实例对象操作时需要,之后就不用了,但是getInstance()方法使用了synchronized关键字修饰,导致整个方法在每次调用时都会同步,这样不知不觉中就降低了程序的执行效率。
线程安全且高效率单例模式
public class ThreadSafeHighEfficiency {
private static volatile ThreadSafeHighEfficiency instance;
private ThreadSafeHighEfficiency() {}
public static ThreadSafeHighEfficiency getInstance() {
if (instance == null) {
synchronized (ThreadSafeHighEfficiency.class) {
if (instance == null) {
instance = new ThreadSafeHighEfficiency();
}
}
}
return instance;
}
}
- 类变量instance增加了修饰符volatile:常用于保证内存可见性和防止指令重排序,但并不保证操作的原子性。volatile是一种稍弱的同步机制,在访问volatile变量时不会执行加锁操作,也就不会执行线程阻塞,因此volatilei变量是一种比synchronized关键字更轻量级的同步机制,但不如synchronized安全。
- getInstance()方法:去掉方法修饰符synchronized,在方法里面使用synchronized块,进入方法后首先判断属性是否为空,如果为空的话就进入同步代码块,进入同步代码块后会再检查一次属性是否为空,如果仍然为空才会创建实例。
这种方法被称为双重检查加锁,属于延迟实例化的实现方式,其保证了程序中只存在一个实例,而且这种实现方式只在第一次的时候才彻底执行所有的代码,当实例创建出来后也不会进入同步代码块,大大减少了性能的耗费。
JVM加载时创建单例模式
public class JVMLoadSingleton {
private static JVMLoadSingleton instance = new JVMLoadSingleton();
private JVMLoadSingleton() {}
public static JVMLoadSingleton getInstance() {
return instance;
}
}
该方法不属于延迟实例化的实现方式,该方法直接在JVM加载类时创建实例对象,对外部只提供获取实例的方法getInstance(),这样外部类调用getInstance()时,方法会直接返回实例对象即可。但是它是在加载类的时候就会创建这个实例,如果创建这个实例非常耗费资源,而程序执行过程中一直没有使用到它,那么将会造成很大的浪费。
参考链接
最后,谢谢各位读者的耐心阅读,如有语句不通顺或者不准确的地方,还请指正!谢谢!(__)