设计模式之单例模式
2020/9/22 14:04:00
本文主要是介绍设计模式之单例模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
什么是单例模式
单例模式属于创建型模式,它提供了一种创建对象的方式,确保只有单个对象被创建。这个设计模式主要目的是想在整个系统中只能出现类的一个实例,即一个类只有一个对象。
比较官方的一段话,可以说解释的很清楚了。
解决了什么问题
由于在单例模式中,一个类只有一个对象,从其特性我们可以很自然的想到单例模式可以
节约空间,节约时间。
- 对于频繁使用的对象,尤其是比较复杂的对象创建,节省了创建对象的时间。
- 因为不需要频繁创建对象,我们的GC压力也减轻了,而在GC中会有STW(stop the world),从这一方面也节约了GC的时间。
结合单列模式的特点,可以顺理成章的知道它的应用场景。
实现方式
那么我们怎么实现一个单例模式呢,我们来思考我们的目标---保证在一个应用中一个类只有一个对象。
于是,我们就有下边的步骤:
- 构造方法私有化
- 在本类中创建一个对象
- 定义一个public方法,在其他程序使用时提供这个已经创建好的对象
饿汉式
<font color='red'>【可以使用】</font>
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
- 优点
实现简单,由于在类加载时就完成了实例化对象,没有线程安全问题。 - 缺点
由于在类加载时就实例化对象,如果后面我们没有用到这个对象,就造成了对资源的浪费。当然,可以忽略不计。
饿汉式的;另一种写法:
<font color='red'>【可以使用】</font>
public class Singleton { private static Singleton instance = null; static { instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return instance; } }
懒汉式
也称饱汉式,与饿汉式不同的是,类的实例在获得的方法中创建。
<font color='red'>【线程不安全,不可使用】</font>
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }
显然,这样写并不保证线程安全。比如有这样两个线程A,B,它们都去调用 getInstance( )方法去拿这个类的对象。由于一开始没有做过实例化,instance为null,A线程在执行完if (null == instance)
后,这时线程B也执行到此处,那A线程还没有完成类的实例化,instance还是null,故两个线程都调用了new去实例化了两个对象。😭
那到这里,就会很简单的想到加锁来解决线程安全的问题了。
<font color='red'>【效率低下,不推荐使用】</font>
public class Singleton { private static Singleton instance = null; private Singleton() { } public static synchronized Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }
观察上面的代码,我们可以看到,在每次调用getInstance() 时,即使该类已经实例化,还是要去做同步,而这种情况只要return就行了。
那思考到这里,顺理成章的能想到,既然这样锁在方法上面效率低下,那就缩小锁的范围,所在这个类上面。于是有下面的实现:
<font color='red'>【线程不安全,不可以使用】</font>
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) { synchronized (Singleton.class) { instance = new Singleton(); } } return instance; } }
嗯,完美!但是仔细看一看,好像哪里不对啊。👿在多线程场景下,我们再捋一下这块代码的执行流程。同样有这样两个线程A,B,它们都去调用 getInstance( )方法去拿这个类的对象。由于一开始没有做过实例化,instance为null,A线程在执行完if (null == instance)
后,拿到了锁。这时线程B也执行到此处,但是由于被锁了,就等待A线程锁的释放。A线程执行new实例化了Singleton,执行完毕后,释放了锁。那B又得到了锁,继续执行Singleton的实例化,悲剧!!!两个线程都调用了new去实例化了两个对象。😭
经过一代又一代人的不懈努力,在此基础上,又有了下面的完美解决方式:
懒汉式双重校验锁
<font color='red'>【可以使用】</font>
public class Singleton { private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) { synchronized (Singleton.class) { if (null == instance) { instance = new Singleton(); } } } return instance; } }
继续上面捋代码的逻辑,等到A线程实例化完成后,释放了锁。这时B线程执行到if ( null == instance )
,由于A线程已经完成了实例化,故直接返回Instance。
这里需要注意的是volatile关键字,JVM会有指令重排的情况,会造成获取到的对象没有被实例化。使用该关键字,可以保证修饰的关键字前后执行顺序唯一,不会进行指令重排。
内部类
<font color='red'>【可以使用】</font>
public class Singleton { private Singleton() { } private static class SingletonHolder{ private static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }
与饿汉式相似,内部类的实现方式都通过在类加载时实例化的方式保证线程安全。
不同点是饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用。但是静态内部类的实现方式在Singleton类被装载时并不会被立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。与饿汉式相比,这种实现方式既没有浪费资源,又能保证线程安全。
枚举
<font color='red'>【推荐使用】</font>
public enum SingletonEnum { instance; private SingletonEnum() { } public void method() { } } class Aa { public static void main(String[] args) { SingletonEnum.instance.method(); } }
在最后比较推荐使用枚举方式实现单例模式,IDEA上新建单例类使用的是第一种饿汉式方法。
欢迎访问个人博客 获取更多知识分享。
这篇关于设计模式之单例模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求