了解单例设计模式的人都知道,单例中涉及的类他在内存之中始终是独一份存在的,如果存在两份则将出现问题,并且单例模式有两种相对比较有特点的形式,那就是饿汉式与懒 汉式单例模式,在本节中我们将会详细讲解单例设计模式的两种形式,并且我们将会讲解如 何在多线程的情况下使用单例设计模式;
1. 饿汉式单例模式
所谓饿汉式单例设计模式,就是将类的静态实例作为该类的一个成员变量,也就是说在 JVM 加载它的时候就已经创建了该类的实例,因此它不会存在多线程的安全问题,详细代码 请看如下:
public class SingleTest { private final static SingleTest instance = new SingleTest(); private SingleTest(){ } public static SingleTest newInstance(){ return instance; } }
可以看到上述代码中的单例不存在线程安全的问题,但是他有一个性能上面的问题,那 就是提前对实例进行了初始化或者说构造,假设构造该类需要很多的性能消耗,如果代码写 成这个样子将会提前完成构造,又假设我们在系统运行过程中压根就没有对该实例进行使用, 那岂不是很浪费系统的资源呢?因此单例设计模式其实还有下一个小节的版本;
2 懒汉式单例模式
所谓懒汉式单例模式的意思就是,实例虽然作为该类的一个实例变量,但是他不主动进 行创建,如果你不使用它那么他将会永远不被创建,只有你在第一次使用它的时候才会被创 建,并且得到保持;请看下一段代码
public class SingleTest2 { private static SingleTest2 instance = null; private SingleTest2() { } public static SingleTest2 newInstance() { if (null == instance) { instance = new SingleTest2(); } return instance; } }
上述的代码就是我们所说的懒汉式单例模式,但是根据上文中的关于线程安全问题的分 析我们不难发出现,instance 有可能会被创建两次,至于原因为什么呢?让我们根据之前的 IO Programming 系列丛书 由于个人能力有限,书中难免会有偏颇之处,希望读到这本书的朋友能够给予我指导和批评,欢迎你们 的指正 知识点来慢慢分析,然后总结出来一个最优的懒汉式单例模式
根据我们之前的知识点,运行中的程序很有可能会出现上图所出现的情况。也就是 A 线程和 B 线程有可能同时执行到了 null==instance 的部分他们判断到了 instance 没有被创建, 因此分别实例化了一个以上的 instance,这样的单例类将是非常危险的,那么我们应该如何 避免多线程引起的问题呢,看到这里您可能想到了用 synchronized 这个关键字来解决问题, 于是有了如下的代码
public synchronized static SingleTest newInstance() { if(null==instance) { instance = new SingleTest(); } return instance; }
但是该方法的效率将是相当低下的,因为每一次调用都要获取锁,判断锁的状态,因此 就会出现解决了安全问题,带来了效率问题,当然安全问题和效率问题本来就是两个很不可 调和的矛盾,但是我们也不应该就此妥协,需要尽我们的智慧既解决了安全问题又带来了最 小的效率影响;我们将程序写成如下的样子
public static SingleTest2 newInstance() { if (null == instance) { synchronized (SingleTest2.class) { if (null == instance) { instance = new SingleTest2(); } } } return instance; }
好的,让我们继续以图示的方式来说明一下,上述代码带来了哪些改变又如何将效率的 损耗降到了最低
通过上述代码的分析,我们不难发现,锁的等待或者争抢最多发生两次,也就是同步代 码块中的代码最多被执行两次,如此一来,安全问题解决了,效率问题也被解决掉了。
3,指令重排优
volatile关键字另一个作用就是禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象,关于指令重排优化参考文章 https://www.cnblogs.com/yuluoxingkong/p/9236077.html
public class SingleTest2 { //以关键字volatile修饰之后,就会阻止JVM对其相关代码进行指令重排,这样就能够按照既定的顺序指执行。 //参考文档 <p>https://www.cnblogs.com/yuluoxingkong/p/9288477.html</p> private volatile static SingleTest2 instance = null; private SingleTest2() { } public static SingleTest2 newInstance() { if (null == instance) { synchronized (SingleTest2.class) { if (null == instance) { instance = new SingleTest2(); //非原子操作,可能会出现指令重排优 } } } return instance; } }