# 设计模式学习笔记(三)之 单例模式 今天是上班的第一天,由于没给安排活,也不知道干啥,于是随便翻翻csdn看到一篇关于单例模式的讲解,觉得写的很不错,讲的简单易懂,为了加深记忆~~(打发时间)~~写下这篇博客。 [原博客地址:单例模式](https://blog.csdn.net/weixin_41949328/article/details/107296517?utm_medium=distribute.pc_category.none-task-blog-hot-6.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-6.nonecase&request_id= "原博客地址") ## 1、什么是单例模式 从书上讲的来说,单例模式,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,单例模式是指在内存中只会创建且仅创建一次对象的设计模式。简单来说就是整个程序有且仅有一个实例。 ## 2、单例模式的两种类型 创建的单例模式有两种类型: - 懒汉式:需要时再去创建对象 - 饿汉式:不管是否需要,在类加载的时候就创建单例对象 ### 2.1 懒汉式 懒汉式在创建对象前需要进行判断对象是否实例化,如果已有实例化对象直接返回,没有则new一个后返回。 代码如下: ``` java public static Singleton{ private static Singleton singleton; private Singleton() {} public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } } ``` ### 2.2 饿汉式 饿汉式在类加载时就创建好实例化的对象,待程序需要时直接返回该对象。 代码如下: ```java public static Singleton{ private static final Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getInstance(){ return singleton; } } ``` ------ 到此两种类型的单例模式都已讲解完成,但是仍然存在某些问题,试想想,在多线程并发的情况下饿汉式单例模式还能正常使用,而懒汉式单例模式真的只有一个实例对象嘛? ## 3、懒汉式单例模式的优化改进 在并发的情况下,会存在还没有实例对象而多个线程进入到判断实例对象是否为null这一步,由于还没有创建实例对象,这些线程都会去创建实例,这样就出现了问题,所以我们需要对该步进行加锁! 改进后代码如下: ```java public static Singleton{ private static Singleton singleton; private Singleton() {} public static Singleton getInstance(){ if (singleton == null) { synchronized(Singleton.class) { // 某个线程获得该锁进行初始化 if(singleton == null){ singleton = new Singleton(); } } } return singleton; } } ``` 因为需要两次判空,且对类对象加锁,该懒汉式写法也被称为:**Double Check(双重校验) + Lock(加锁)** 上面这段代码已经近似完美了,但是还存在最后一个问题:指令重排(这块是看完[这篇博客](https://blog.csdn.net/weixin_41949328/article/details/107296517?utm_medium=distribute.pc_category.none-task-blog-hot-6.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-6.nonecase&request_id= "这篇博客")后才知道的) ## 4. 使用volatile防止指令重排 创建一个对象,在JVM中会经过三步: (1)为singleton分配内存空间 (2)初始化singleton对象 (3)将singleton指向分配好的内存空间 指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能 在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。 使用volatile关键字优化后的代码: ```java public static Singleton{ private static volatile Singleton singleton; private Singleton() {} public static Singleton getInstance(){ if (singleton == null) { synchronized(Singleton.class) { // 某个线程获得该锁进行初始化 if(singleton == null){ singleton = new Singleton(); } } } return singleton; } } ``` ## 5、总结 单例模式分为懒汉式和饿汉式两种写法,之前课堂上讲的由于没怎么听过对这些都不太熟悉,写完这篇博客后算是基本了解单例模式了,以后在实际代码中用到时也不会显得迷茫,要去考虑多线程的这种情况,因为实际项目和理想上的情况总是不一样的,需要考虑全面! **注意事项:** (1)在开发中如果对内存要求比较高,那么使用懒汉式写法,可以在特定时候才创建该对象; (2)不必考虑内存情况则推荐使用饿汉式写法,因为简单不易出错!且没有任何并发安全和性能问题 (3)为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序 最后修改:2020 年 07 月 17 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏
1 条评论
你的才华横溢,让人敬佩。http://www.qzershouche.com