1.单例设计模式核心作用:

保证一个类只有一个实例,并且提供了访问该实例的全局访问点

2.常见应用场景:

  • window的任务管理器
  • 项目中读取配置文件一般也是一个单例模式
  • 数据库连接池的设计也是采用单例模式,因为数据库连接是一种数据库资源
  • 操作系统的文件管理系统,也是单例模式,一个操作系统只能有一个文件系统
  • Application也是单例的应用(Servlet编程或者Android的Application类)
  • 在Spring中,每个bean默认也是单例的,这样的有点儿事Spring容器可以管理
  • 在Servlet编程中每个Servlet也是单例的
  • 在Spring MVC和Struts1框架中控制器对象也是单例

3.单例模式的优点

  • 由于单例模式只生产一个对象,减少了系统开销,当一个对象的产生需要的资源比较多的时候,比如读取配置文件、产生其它依赖对象时,则可以在应用启动的时候直接产生一个单例对象,然后永久驻存内存的方式来解决。
  • 单例模式可以在系统设置全局访问点,优化共享资源的访问。例如可以设计一个单例类,负责所有数据表的映射。

4.常见5中单例模式的实现方式:

主要

饿汉式:线程安全,调用效率高。但是不能延时加载

懒汉式:线程安全,调用效率不高。但是可以延迟加载

其它:

双重检锁式:由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用

静态内部类式:线程安全,调用效率高,而且可以延迟加载

枚举单例:线程安全,调用效率高,不可延迟加载

饿汉式的示例代码:

public class Singleton01 {//类初始化的时候,立即加载这个对象(没有延时加载的优势)。加载类时,是线程安全的private static Singleton01 instance = new Singleton01();private Singleton01(){}//方法没有同步调用效率高public static Singleton01 getInstance(){return instance;}
}

饿汉式单例模式的代码中,static变量会在类装载的时候进行初始化,此时不会涉及到多个线程对象访问该对象的问题。虚拟机会保证只会装载一次该类,肯定不会发生并发访问的问题,因此可以省略synchronized关键字

问题:如果仅仅是加载本类,而不是要调用getInstance,甚至永远都没有调用,则会造成资源浪费。

懒汉式的示例代码

 1 package com.chunjiangchao.pattern.singleton;
 2 /**
 3  * 测试懒汉式单例模式
 4  */
 5 public class Singleton02 {
 6     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
 7     private static Singleton02 instance = null;
 8     private Singleton02(){}
 9     方法同步,调用效率低!
10     public static synchronized Singleton02 getInstance(){
11         if(instance == null)
12             instance = new Singleton02();
13         return instance;
14     }
15 }

要点:延迟加载,懒加载真正用到的时候才会选择加载

问题:

资源利用率高了,但是每次调用getInstance()方法都要同步,并发效率较低。

双重检锁实现

 1 package com.chunjiangchao.pattern.singleton;
 2 /**
 3  * 测试DCL(双重检锁)单例模式
 4  *
 5  */
 6 public class Singleton03 {
 7     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
 8     private volatile static Singleton03 instance = null;
 9     private Singleton03(){}
10     代码块同步,调用效率要比同步方法要快一些,由于JVM的原因在高并发的情况下会出现问题
11     public static  Singleton03 getInstance(){
12         if(instance == null){
13             synchronized (Singleton03.class) {
14                 if(instance == null)
15                     instance = new Singleton03();
16             }
17         }
18         return instance;
19     }
20 }

Volatile关键字的作用:

  • 防止指令重排序如:instance = new Singleton03();这条操作分三步执行,1、分配内存;2、进行初始化;3、将生成对象的堆内存地址赋值给instance变量。这些指令中2、 3的位置可能会进行重排序,导致在获取到对象的时候,该对象还没有进行初始化。volatitle可以防止这种指令进行重排序。
  • 当然Volatile还有一个作用是同步CPU缓存区和内存中的变量

提高了执行 的效率,不必每次获取对象的时候都要进行同步,只有第一次才会进行同步创建。

问题:

由于编译器优化的原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用。但是我们可以在instance前面添加volatile关键字,这样就没问题了。

静态内部类实现方式:(懒加载方式)

 1 package com.chunjiangchao.pattern.singleton;
 2 /**
 3  * 静态内部类单例模式
 4  * 这种方式:线程安全,调用效率高,并且实现了延时加载!
 5  */
 6 public class Singleton04 {
 7     private Singleton04(){}
 8     public static  Singleton04 getInstance(){
 9         return Inner.instance;
10     }
11     private static class Inner{
12         private static final Singleton04 instance = new Singleton04();
13     }
14 }

外部类没有static属性,则不会像饿汉式那样,上来就把对象造出来了

只有真正调用getInstance才会加载静态内部类。加载类时是线程安全的。instance 是static final类型,保证了内存中只有这样一个实例存在,而且只被赋值一次,从而保证了线程安全性。

兼并并发高效调用和延迟加载的优势。

换一句户说:静态内部有具备饿汉式和延迟加载的优势。

枚举实现单例:

 1 package com.chunjiangchao.pattern.singleton;
 2 /**
 3  * 枚举式实现单例模式(没有延时加载)
 4  */
 5 public enum Singleton05 {
 6     instance;//这个枚举元素,本身就是单例对象!
 7     public void operation(){
 8         //添加需要的操作
 9     }
10 }

优点:实现简单;枚举本身就是单例。由JVM从根本上提供保障。避免反射和序列化的漏洞

缺点:无延迟加载

5.如何选用这五种单例模式?

单例对象占用资源少,不需要延迟加载:

枚举好于饿汉式

单例对象占用资源大,需要延迟加载

静态内部类好于懒汉式

6.问题

反射可以破解上面(不包含枚举)的实现方式(防止的做法是在构造方法中手动抛出异常)

反序列化可以破解(不包含枚举)的实现方式

可以通过定义readResolve防止获得不同对象。反序列化的时候,如果对象所在的类定义了readResolve()方法(一种回调方法),返回自己创建的那个对象。

示例代码如下:

 1 package com.bjsxt.singleton;
 2
 3 import java.io.ObjectStreamException;
 4 import java.io.Serializable;
 5
 6 /**
 7  * 测试懒汉式单例模式(如何防止反射和反序列化漏洞)
 8  *
 9  */
10 public class SingletonDemo6 implements Serializable {
11     //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
12     private static SingletonDemo6 instance;
13
14     private SingletonDemo6(){ //私有化构造器
15         if(instance!=null){
16             throw new RuntimeException();
17         }
18     }
19
20     //方法同步,调用效率低!
21     public static  synchronized SingletonDemo6  getInstance(){
22         if(instance==null){
23             instance = new SingletonDemo6();
24         }
25         return instance;
26     }
27
28     //反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
29     private Object readResolve() throws ObjectStreamException {
30         return instance;
31     }
32
33 }

测试代码如下:

 1 package com.chunjiangchao.pattern.singleton;
 2
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileOutputStream;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutputStream;
 8 import java.lang.reflect.Constructor;
 9
10 /**
11  * 测试单例模式
12  */
13 public class SingletonDemo {
14     public static void main(String[] args) throws Exception {
15         Singleton01 singleton01 = Singleton01.getInstance();
16         Singleton01 singleton02 = Singleton01.getInstance();
17         System.out.println(singleton01.hashCode());
18         System.out.println(singleton02.hashCode());
19
20         /**
21         //测试反射
22         try {
23             Class<?> clazz = Class.forName("com.chunjiangchao.pattern.singleton.Singleton06");
24             Constructor<?> constructor = clazz.getDeclaredConstructor(null);
25             constructor.setAccessible(true);
26             Singleton06 singleton06 = (Singleton06) constructor.newInstance(null);
27             System.out.println(singleton06);
28             System.out.println(Singleton06.getInstance());
29         } catch (Exception e) {
30             e.printStackTrace();
31         }
32         //打印的结果:
33         //com.chunjiangchao.pattern.singleton.Singleton06@495c83b2
34         //com.chunjiangchao.pattern.singleton.Singleton06@58ca40be
35          */
36         //通过序列化获取一个对象
37         Singleton06 instance = Singleton06.getInstance();
38         FileOutputStream fos =  new FileOutputStream("/Users/xxx/Desktop/a.txt");
39         ObjectOutputStream oos = new ObjectOutputStream(fos);
40         oos.writeObject(instance);
41         oos.close();
42         fos.close();
43
44         FileInputStream fis = new FileInputStream("/Users/xxx/Desktop/a.txt");
45         ObjectInputStream ois = new ObjectInputStream(fis);
46         Object obj = ois.readObject();
47         ois.close();
48         fis.close();
49         System.out.println(instance);
50         System.out.println(obj);
51     }
52
53 }

7、测试几种模式的效率

五种单例模式在多线程环境下效率测试:使用CountDownLatch同步工具类,允许当前线程等待其它一组线程都执行完毕后,执行当前线程的后续操作。

countDown()当前线程执行此方法,计数器-1

await()调动此方法会一直阻塞当前线程,知道计数器为0的时候重新运行当前线程

下面示例代码来演示当前线程执行的效率:

 1 package com.chunjiangchao.pattern.singleton;
 2
 3 import java.util.concurrent.CountDownLatch;
 4
 5 /**
 6  * 五种单例模式的性能测试
 7  *
 8  */
 9 public class SingletonDemo02 {
10
11     public static void main(String[] args) throws InterruptedException {
12         int threadNum = 10;
13         long beginTime = System.currentTimeMillis();
14         final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
15         for (int i = 0; i < threadNum; i++) {
16             new Thread(new Runnable() {
17
18                 @Override
19                 public void run() {
20                     for (int j = 0; j < 20000; j++) {
21                         //Singleton01.getInstance();//18ms
22                         //Singleton02.getInstance();//49ms
23                         //Singleton03.getInstance();//22ms
24                         //Singleton04.getInstance();//32ms
25                         Singleton05 instance = Singleton05.instance;//9ms
26                     }
27                     countDownLatch.countDown();
28                 }
29             }).start();
30         }
31         //让主线程进行阻塞
32         countDownLatch.await();
33         long endTime = System.currentTimeMillis();
34         System.out.println(endTime-beginTime);
35     }
36
37 }

单例对象占用资源少,不需要延迟加载:

转载于:https://www.cnblogs.com/chun-jiang-chao-de-gu-shi/p/5376574.html

GOF设计模式之1:单例设计模式相关推荐

  1. 多个if用什么设计模式_抽丝剥茧——单例设计模式

    单例设计模式 兄弟们好,今天是最后一个设计模式了,也是我们最熟悉的单例设计模式,可以说这个设计模式是我们最先接触到的设计模式了.想当年学习JavaSE的时候,老师聊到一个「饿汉式和懒汉式」,我还纠结了 ...

  2. 设计模式之简单单例设计模式

    参考资料:[作者:刘伟  http://blog.csdn.net/lovelion] 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 ...

  3. Java设计模式(五):单例设计模式

    1. 应用场景 一个无状态的类使用单例模式节省内存资源. 比如说线程池.缓存.对话框.设置偏好和注册表对象.日志对象.充当打印机.显卡等设备的驱动程序对象. 2. 概念 确保一个类只有一个实例,并提供 ...

  4. 【C++设计模式】Singleton 单例设计模式与线程安全

    文章目录 何为设计模式(Design Pattern) 单例模式(Singleton) 什么是单例模式? 饿汉单例模式 懒汉单例模式 懒汉单例模式的线程安全版本 经典加锁版本 优化版本:双重检查锁 使 ...

  5. java饿汉式有啥作用,Java面试 - 什么是单例设计模式,为什么要使用单例设计模式,如何实现单例设计模式(饿汉式和懒汉式)?...

    什么是单例设计模式? 单例设计模式就是一种控制实例化对象个数的设计模式. 为什么要使用单例设计模式? 使用单例设计模式可以节省内存空间,提高性能.因为很多情况下,有些类是不需要重复产生对象的.如果重复 ...

  6. 第 5 章 单例设计模式

    第 5 章 单例设计模式 1.单例设计模式介绍 所谓类的单例设计模式, 就是采取一定的方法保证在整个的软件系统中, 对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法). ...

  7. Java设计模式——单例设计模式/权限修饰符的使用

    1. 单例模式含义 所谓的单例设计模式,就是采取一定的方法保证整个软件系统中,某个类只能存在一个对象实例. 单例设计模式 2. 单例设计模式的两种实现方法 饿汉式:不管是否需要该实例,我事先就把该实例 ...

  8. 23种设计模式:单例设计模式(饿汉式 VS 懒汉式)

    23种设计模式:单例设计模式(饿汉式 VS 懒汉式) 每博一文案 世事浮沉,有太多的责任需要我们担当,生活中总有些挫折和磨难,让我们觉得快要杠不住了. 但当我们咬牙坚持过那段难熬的时光后,发现并没有想 ...

  9. 黑马程序员——JAVA笔记——单例设计模式

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 在JAVA开发中单例设计模式是一种常见的的设计模式,一般来说单例设计模式分为懒汉式和饿汉式. 然 ...

  10. 【Java】day9--main方法、单例设计模式、继承、方法重写部分知识点总结

    (一)main方法 jvm调用main方法,jvm也是一个程序     main方法详解:         public:公共  保证该类在任何情况下,jvm都对其方法可见.         stat ...

最新文章

  1. 专题 16 基于UDP的通信程序设计
  2. python绘制条形图-python3使用matplotlib绘制条形图
  3. 船舶工程跨考计算机硕士,敢挑战这6个专业,考研选择跨考也不虚
  4. VTK:图表之InEdgeIterator
  5. c++primer 12.3.1文本查询程序
  6. Nginx 模块的使用
  7. 解决 No projects are available for deployment to this server!
  8. e480 黑苹果_Thinkpad E480黑苹果组建Fusion Drive——鱼和熊掌我都要
  9. 东野圭吾梦幻花读后感_《梦幻花》精选读后感
  10. Docker tomcat 多版本环境搭建
  11. 软件测试团队队名,电子设计大赛队名
  12. cimfax显示无法连接到服务器,CimFAX传真机软件
  13. php开发h5游戏教程,HTML5游戏框架cnGameJS开发实录-实现动画原理
  14. 在Visual Studio.NET中更改颜色-黑色与白色
  15. js面试必备:原形链
  16. python最小二乘法_最小二乘法(least sqaure method)
  17. windows用c++获取cpu温度
  18. iOS开发-极光推送SDK使用笔记
  19. 调试程序路径“C:\Users\{用户名}\vsdbg\vs2017u5”无效
  20. 关于火狐浏览器开发者工具使用

热门文章

  1. 【BZOJ2054】疯狂的馒头(并查集)
  2. POJ1459 Power Network —— 最大流
  3. EJB是什么?EJB的概念分析与理解(copy)
  4. SharePoint Framework 构建你的第一个web部件(三)
  5. 最大和 -- 最大子矩阵
  6. 11.m进制转十进制
  7. PHP中过滤数组中的元素
  8. 添加多个tomcat服务目录
  9. CentOS7安装redis并配置外网可访问(局域网可参考)
  10. 微信小程序 WXBizDataCrypt 解密 报错