GOF设计模式之1:单例设计模式

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php 管理 mysql 数据库 代码_PHP5对Mysql5的任意数据库表的管理代码示例(三)

续&#xff1a;点击编辑一个条目会跳转至edit.php//edit.phpEditing an entry from the databaseEdit an entry$database "sunsite";$tablename $_REQUEST[tablename];echo "Data from $tablename";MySQL_connect("localhost","root"…

封装EF code first用存储过程的分页方法

一年半没有做过MVC的项目了&#xff0c;还是很怀念&#xff08;因为现在项目还是原来的ASPX&#xff09;&#xff0c;个人还是喜欢mvc&#xff0c;最近又开始重拾MVC&#xff0c;感觉既熟悉又陌生。 记录一下封装好的分页代码 首先先说下 我使用EF codefirst的目的. 是因为可以…

apache httpd mysql_Centos7安装配置Apache(httpd)+php+mysql+phpMyAdmin

一、安装mysql执行命令&#xff1a;rpm -ivh mysql-community-release-el7-5.noarch.rpm执行 yum -y install mysql mysql-server mysql-devel&#xff0c;安装成功。再次使用该命令&#xff0c;然后得到以下信息打开mysql服务 &#xff1a;service mysqld start进入mysql客户端…

如何:创建公钥/私钥对

要使用强名称为程序集签名&#xff0c;必须具有公钥/私钥对。 这一对加密公钥和加密私钥用于在编译过程中创建强名称程序集。 您可以使用强名称工具 (Sn.exe) 来创建密钥对。 密钥对文件通常具有 .snk 扩展名。 注意 在 Visual Studio 中&#xff0c;C# 和 Visual Basic 项…

用户设置及用户默认设置

1、创建 其中&#xff0c;Root.plist中为设置 2、读取应用中的设置 3、在应用中修改默认设置 4、注册默认值 5、保证设置有效&#xff08;我们注册所有控制器&#xff0c;以便接收从暂停执行状态唤醒的应用发送出来的通知&#xff09; 每个控制器都订阅我们关注的通知。 6、撤销…

activemq jdbc mysql_activeMQ JDBC Master Slave

ActiveMQ集群应用2\ ActiveMQ JDBC 主从集群3\ 解决单点故障&#xff1a;JDBC Master Slave本人应用的为集群方案中的一种&#xff0c; JDBC Master Slave &#xff0c;说明及好处就看如上外链文章了&#xff0c;上正题&#xff0c;方案如何实施及程序如何调用&#xff11;&…

AsyncTask知识整理笔记

Android的AsyncTask比Handler更轻量级一些&#xff0c;适用于简单的异步处理。&#xff08;但是&#xff0c;要注意&#xff01;&#xff01;&#xff01;&#xff01;使用更轻量级&#xff0c;实际执行却反之&#xff0c;因为AsyncTask里封装了Handler&#xff0c;本质也是使用…

mysql sql语句 入门_mysql(3)mysql的sql语句入门

1.登录mysql与退出mysql登录&#xff1a;mysql -u账号 -p密码 -h主机地址 -P端口号 -e 执行的sql语句&#xff1b; //密码一般不直接写出&#xff0c;回车后隐藏写入 &#xff0c;-h可以不写默认是localhost退出&#xff1a;quit&#xff0c;exit &#xff0c;ctrlc备注&…

orcale 基本查询(1)

orcale 基本查询      查询当前用户下的所有表&#xff1a;     select * from tab;      查询表结构&#xff1a;   desc 表名设置行宽&#xff1a;    set linesize 120;设置列宽&#xff1a;    数值类型&#xff1a;      col 列名 for…

优秀的Android资源

今天&#xff0c;收藏了一下“优秀的Android资源”&#xff0c;以后有时间也学习学习。 一、开发工具 开发android第一步就是安装开发工具SDK&#xff0c;国内有一些机构和公司对些作了境像&#xff0c;这个网站作了详细统计列表&#xff1a;http://www.androiddevtools.cn/&am…

mysql视图表修复_mysql中含有视图数据库在恢复数据时视图变成数据表的解决方法...

# version: 1.0 for linux# method: 全备份mysql数据# author: fengzhanhai# history: create program V1.0 20121101 by fengzhanhai# tasks: * 1 * * * /home/db-backup/mysql/mysqlbk.sh >> /dev/null 2>&1#conf---------UserNamesdmtvm…

LeetCode OJ 160. Intersection of Two Linked Lists

Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: A: a1 → a2↘c1 → c2 → c3↗ B: b1 → b2 → b3begin to intersect at node c1. Notes: If the …

监控mysql业务数据分析_MySQL数据库监控指标之执行性能总结

查询性能MySQL 用户监控查询延迟的方式有很多&#xff0c;既可以通过 MySQL 内置的指标&#xff0c;也可以通过查询性能模式。从 MySQL 5.6.6 版本开始默认启用&#xff0c;MySQL 的 performance_schema 数据库中的表格存储着服务器事件与查询执行的低水平统计数据。1、perform…

redis集群关闭 启动报错_使用虚拟机搭建 Redis 集群,实现数据库的负载均衡功能。...

实操题目:使用虚拟机搭建 Redis 集群&#xff0c;实现数据库的负载均衡功能。并使用图文描述整个过程。先创建集群:①创建集群需要使用ruby脚本,所以要先安装ruby环境 安装ruby环境:yum install ruby 安装ruby的包管理器:yum install rubygems②进入到redis的安装目录下的 src …

转载:Java编程风格与命名规范整理

转载自&#xff1a;传送门 不想复制&#xff0c;点进去看喽23333333转载于:https://www.cnblogs.com/Dumblidor/p/5394390.html

[转]完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法

HTML5的语义化标签以及属性&#xff0c;可以让开发者非常方便地实现清晰的web页面布局&#xff0c;加上CSS3的效果渲染&#xff0c;快速建立丰富灵活的web页面显得非常简单。 HTML5的新标签元素有&#xff1a; <header> 定义页面或区段的头部&#xff1b; <footer&…

mysql change index_MySQL · 引擎特性 · Innodb change buffer介绍

前言在前面几期月报我们介绍了undo log、redo log以及InnoDB如何崩溃恢复来实现数据ACID的相关知识。本期我们介绍另外一种重要的数据变更日志&#xff0c;也就是InnoDB change buffer。 Change buffer的主要目的是将对二级索引的数据操作缓存下来&#xff0c;以此减少二级索引…

获取当前日期时间并格式化 - 常见格式

获取系统当前日期时间的方法&#xff1a; 1 //获取系统当前日期时间2 Date.prototype.format function (format) {3 var o {4 "M": this.getMonth() 1, //month5 "d": this.getDate(), //day6 "h": this.getHour…

dncnn图像去噪_一种基于DnCNNs改进的图像降噪方法与流程

本发明涉及图像处理技术领域&#xff0c;具体涉及一种基于dncnns改进的图像降噪方法。背景技术&#xff1a;随着科技进步&#xff0c;新的图像技术在逐渐推广&#xff0c;在日常生活中人们对于图像的要求也越来越高&#xff0c;针对阴天或夜晚等弱光条件下拍摄的图像具有噪点较…

fan怎么写 jin_拼音为fan的字

7画匥匥fn ㄈㄢˊ◎古同“笲”。3画凡凡fn ㄈㄢˊ1、平常的&#xff0c;不出奇的&#xff1a;平凡。凡庸。凡夫俗子。2、指人世间(宗教或迷信的说法)&#xff1a;凡尘。凡心。3、所有的&#xff1a;凡年满十八岁的公民&#xff0c;都有选举权与被选举权。凡是。4、总共&#xf…