单例模式 (Singleton)
单例模式(Singleton)是 Java 中常见的也是最基础的设计模式,单例对象在Java应用的 JVM 中只存在一个实例。单例模式的好处有很多,例如频繁的对相同对象进行new操作会加大系统内存的损耗,而单例对象只创建一次,降低了内存损耗,同时也减轻了垃圾回收(GC)的压力。
废话少说,直接上代码实例,让我们通过代码实例对单例对象进行进一步的了解。
代码实例
下图是单例对象的简单实现:
单例对象的简单实现
以上单例对象的创建可以满足常见的基本需求,但是在高并发的情况下就会出现问题,因为这种实现是丝毫没有 线程安全 可言的。所以在 多线程 的环境下我们需要对以上单例对象中的实例创建做一些修改。我们知道在多线程情况下可以通过给对象加锁而实现线程安全,也就是给这个方法添加 synchronized 关键字,使这个方法成为通过方法。下图为代码实例:
单例对象 实例化 的同步方法
在我们以前的文章中讲过,在多线程情况下使用synchronized关键字为资源进行加锁,锁住的 区块 是越小越好的。因为锁住的区块越大对资源的损耗越大,性能也就有所下降。而且在这个实例中我们只需要在第一次实例化对象的时候进行加锁就可以了。下图为改进后代码实例:
单例对象实例化的同步代码块
以上重新改进后的代码好像对上面我们提到的所有问题都进行了解决。但是我们还是忽略掉了一个问题:在Java中,创建对象与赋值操作是分开进行的。
例如:有AB两个 线程 ,两个线程都持有了该对象并进入if判断条件,但是A线程首先进入了同步代码块并判断当前对象没有被实例化,因此去执行instance = new Singleton()语句要求JVM进行实例化Singleton对象。 这个时候Java的JVM会为Singleton实例分配空白内存,并且赋值给了instance,然后A线程离开了同步代码块。随后B线程排队进入同步代码块进行与A线程相同的操作,因为此时instance已经有值了,所以B线程离开了同步代码块。此时B线程想要使用Singleton的实例,但是发现这个实例并没有被JVM初始化,抛出了异常。
为什么会出现以上的情况呢,这个就是我们上面讲到的Java应用的JVM中创建对象与赋值操作是分开进行的。在以上实例中A线程要求JVM实例化Singleton对象的时候,JVM只是在内存中开辟了空白内存,并没有开始实例化这个对象。而之后B线程立马就要使用未被实例化的Singleton对象,因此就报错了。针对以上问题我们对代码再次进行改进,以下是代码实例:
单例对象实例化实例
以上代码实例中,我们把单例对象的判断与创建区分开来,在创建单例对象的时候添加同步方法。这样在创建对象的方法执行结束的时候JVM就一定会在内存中把个该对象实例化掉,而不是开辟一块空白内存返回。
结语
我们今天讲的四个单例模式创建方式只是众多方法中的几种,这几种实现方式可以解决大部分情况下的单例创建,但是不是绝对的,也不是可以完全可以解决掉开发过程中遇到的所有问题。通过对单例模式的学习我们可以发现,单例模式理解起来很简单,但是具体应用过程中实现起来还是很麻烦的,因此需要大家在实际应用过程中根据实际情况去做响应的处理。大家如果有在单例模式开发的过程中遇到的不同的问题可以在留言板中与我和各位看官分享。
感谢各位的阅读,大家可以关注本人头条号以便大家获取最新咨询。