一篇博客读懂设计模式之---单例模式

一篇博客读懂设计模式之---单例模式

 

一。  单例模式

 

      单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处

1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

 

 

总体来说,单例模式应用的场景一般发现在以下条件下:

  (1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。

  (2)控制资源的情况下,方便资源之间的互相通信。如线程池等。

 

有以下的特点:(eg。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。为了避免不一致状态

 

 

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

优缺点

 

优点: (频繁new同一个)
    1.在单例模式中,活动的单例只有一个实例,对单例类所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象访问一个实例 
    2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
    3.提供了对唯一实例的受控访问。 
    4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
    5.允许可变数目的实例。 

    6.避免对共享资源多重占用

 

缺点: (保存状态,扩展职责)
    1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
    2.由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。 
    3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 

    4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态丢失

 

1)*懒汉式单例:Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
ps:以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,

//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton1 {//1、第一步先将构造方法私有化private Singleton1(){}//2、然后声明一个静态变量保存单例的引用private static Singleton1 single = null;//3、通过提供一个静态方法来获得单例的引用//不安全的public static Singleton1 getInstance(){if (single == null){single = new Singleton1();}return  single;}
}
//懒汉式单例.保证线程安全
public class Singleton2 {//1、第一步先将构造方法私有化private Singleton2(){}//2、然后声明一个静态变量保存单例的引用private static Singleton2 single = null;//3、通过提供一个静态方法来获得单例的引用//为了保证多线程环境下正确访问,给方法加上同步锁synchronized//慎用  synchronized 关键字,阻塞,性能非常低下的//加上synchronized关键字以后,对于getInstance()方法来说,它始终单线程来访问//没有充分利用上我们的计算机资源,造成资源的浪费public static synchronized Singleton2 getInstance(){if(single == null){single = new Singleton2();}return single;}
}
//懒汉式单例.双重锁检查
public class Singleton3 {//1、第一步先将构造方法私有化private Singleton3(){}//2、然后声明一个静态变量保存单例的引用private static Singleton3 single = null;//3、通过提供一个静态方法来获得单例的引用//为了保证多线程环境下的另一种实现方式,双重锁检查//性能,第一次的时候public static Singleton3 getInstance(){synchronized (Singleton3.class){if (single == null){single = new Singleton3();}return single;}}
}

2)*饿汉式单例:(饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。)


public class Singleton4 {/*** 懒汉式单例* 最常用的一种模式就是这种*///1. 私有化构造器//相当于有一个默认的public的无参的构造方法,就意味着在代码中随时都可以new出来private Singleton4(){}//2. 生成一个静态内部构造类//private 私有的保证别人不能修改//static 保证全局唯一private static class LazyLoader{//final 为了防止内部误操作,代理模式,GgLib的代理模式//静态内部类是为了防止反射获取属性private static final Singleton4 INSTANCE = new Singleton4();}//3、同样提供静态方法获取实例//final 确保别人不能覆盖public static Singleton4 getInstance(){//【静态方法】中的逻辑,是要在用户调用的时候才开始执行的//方法中实现逻辑需要分配内存,也是调用时才分配的//【区别】与静态块的最大区别return LazyLoader.INSTANCE;}
}

还有一种注册式:


//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton5 {private static Map<String,Singleton5> map = new HashMap<>();static {Singleton5 single = new Singleton5();map.put(single.getClass().getName(), single);}//静态工厂方法,返还此类惟一的实例private Singleton5(){}public static Singleton5 getInstance(String name) throws ClassNotFoundException {if(name == null){name = Singleton5.class.getName();}if(map.get(name) == null){try{map.put(name,(Singleton5)Class.forName(name).newInstance());} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}}return map.get(name);}
}


3) 饿汉式和懒汉式区别(重点)

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:

1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是下面的1、2、3,这三种实现在资源加载和性能方面有些区别。

2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

1. 在getInstance方法上加同步

2. 双重检查锁定:

3. 静态内部类:

<span style="color:#3333ff">public class Singleton {    private static class LazyHolder {    private static final Singleton INSTANCE = new Singleton();    }    private Singleton (){}    public static final Singleton getInstance() {    return LazyHolder.INSTANCE;    }    
}    </span>

 

至于1、2、3这三种实现又有些区别,

 

第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

线程安全:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:public String getName() {  return name;  }  public void setName(String name) {  this.name = name;  }  public void printInfo() {  System.out.println("the name is " + name);  }  }  

 

public class TMain {  public static void main(String[] args){  TestStream ts1 = TestSingleton.getInstance();  ts1.setName("jason");  TestStream ts2 = TestSingleton.getInstance();  ts2.setName("0539");  ts1.printInfo();  ts2.printInfo();  if(ts1 == ts2){  System.out.println("创建的是同一个实例");  }else{  System.out.println("创建的不是同一个实例");  }  }  
}  

 

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

 

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

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

相关文章

计算机会计知识竞赛,全国企业会计信息化知识竞赛试题及答案

全国企业会计信息化知识竞赛试题及答案第 1 题(单选)下列各项中&#xff0c;符合会计软件不可逆的记账功能要求的是( A )A.记账标记不得通过任何操作予以取消B.不可以用红字凭证更正记账错误C.可以删除已经记账的记账凭证D.可以进行反记账第 2 题(单选)下列关于信息系统自动生成…

使用缓冲字节流:BufferedInputStream与BufferedOutputStream读写数据

功能逻辑&#xff1a;将一个视频文件通过缓冲字节输入流进行读取&#xff0c;然后再通过缓冲字节输出流将其重新拷贝输出。public static void main(String[] args) {try {FileInputStream fis new FileInputStream("Rick_and_Morty.mkv");//第二个参数指定了缓冲区…

多媒体计算机技术的主要特点,多媒体技术主要特点?

满意答案01and032013.11.24采纳率&#xff1a;41% 等级&#xff1a;11已帮助&#xff1a;5479人多媒体技术有以下几个主要特点&#xff1a;(1)集成性 能够对信息进行多通道统一获取、存储、组织与合成。(2)控制性 多媒体技术是以计算机为中心&#xff0c;综合处理和控制多媒…

java io流的学习总结~~

java io流的学习总结~~流的概念和作用流是一组有顺序的&#xff0c;有起点和终点的字节集合&#xff0c;是对数据传输的总称或抽象。即数据在两设备间的传输称为流&#xff0c;流的本质是数据传输&#xff0c;根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进行数据…

计算机组成原理第二章数据,计算机组成原理第二章数据在计算机中的表示

计算机组成原理第二章数据在计算机中的表示 (91页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;14.90 积分第二章 数据在计算机中的表示 n 概述 n 字符编码 n 中文编码 n 逻辑数据 n 数值数据 n 校验…

linux 指令学习总结~~~

linux 指令学习总结~~~目录操作&#xff1a;mkdir $HOME/testFolder&#xff08;创建一个目录&#xff09; cd ./testFolder(cd $HOME/testFolder)&#xff08;切换目录&#xff09; cd ..&#xff08;切换到上级目录&#xff09; mv $HOME/testFolder /var/tmp/&#xff08;移…

Java移位运算

博客理由&#xff1a;景区APP项目中涉及到分布式ID设计。分别有几种ID的设计方式&#xff1a;一种是数据库的自增&#xff0c;但是局限很大&#xff1b;第二种uuid&#xff0c;直接采用jdk自带的uuid生成即可&#xff1b;第三种是Twitter的Snowflake(雪花)ID生成技术&#xff0…

计算机的外部设备 硬盘,外部设备采用SMB协议想共享电脑硬盘下的文件

您好&#xff01;很抱歉这么晚回复您&#xff0c;请您尝试以下的步骤&#xff1a;1、右键点击”此电脑“&#xff0c;选择“管理”&#xff0c;展开“本地用户和组”&#xff0c;右键点击“Guest”&#xff0c;设置密码为空密码&#xff1b;取消帐户已禁用&#xff0c;点选“用…

史上最全的ubuntu服务器搭建环境教程~~~

ubuntu服务器搭建环境~~~ 1. 先安装xshell&#xff1a;远程服务器连接&#xff08;取代直接在浏览器 上 访问&#xff09; 2. 安装xftp&#xff08;ftp文件传输&#xff09;直接双击红色圈圈即可3. 安装mysql数据库&#xff1a;指令&#xff1a;sudo apt-get updatesudo apt-ge…

Apache工具包方法——Hex.encodeHexString(byte[] data)源码浅析

【2019-07-02 注&#xff1a;标题是Hex.encodeHexString(byte[] data) 的源码解析&#xff0c;但在实际测试过程中&#xff0c;改了方法名称&#xff0c;内部实现还是完全一样的。】 最近正在研究加密的相关方法和思想&#xff0c;有时候会用到byte类型的数组密钥或者密文&…

Spring Boot————简介与Spring时代的承接

引言 最近&#xff0c;公司正在开发一个景区系统的项目&#xff0c;涉及到Spring Boot框架的使用&#xff0c;在此记录下Spring Boot框架应该知道的基础知识。 一、产生 SpringBoot是顺应现在微服务&#xff08;MicroServices&#xff09;理念而产生的一个微框架&#xff08…

计算机考试400,400作文:电脑考试

400作文&#xff1a;电脑考试今天下午第三节课是大家最爱的电脑课&#xff0c;第三节课的上课铃还没敲响&#xff0c;大家就早早来到电脑室门口&#xff0c;拍好整齐的队伍&#xff0c;等待电脑金老师来带领我们进电脑室。终于&#xff0c;金老师面带微笑从办公室走到了我们这边…

史上最容易理解的暴力递归和动态规划~~

史上最容易理解的暴力递归和动态规划~~介绍递归和动态规划暴力递归&#xff1a;1&#xff0c; 把问题转化为规模缩小了的同类问题的子问题2&#xff0c; 有明确的不需要继续进行递归的条件(base case)3&#xff0c; 有当得到了子问题的结果之后的决策过程 4&#xff0c; 不记录…

英雄联盟与人生奋斗的类比讨论

自从去年毕业之后就已经很少玩英雄联盟了&#xff0c;最近半年更是一次都没碰过这个游戏了。 更新还是异常的频繁&#xff0c;打斗还是异常的激烈&#xff0c;比赛还是异常的水&#xff08;S系列中国队的表现&#xff09;。 我今天并不是想追忆曾经的激情&#xff0c;而是想把…

如何最好手机硬件测试软件,手机硬件出现故障?简单几部教你自己动手检测! _手机资讯...

很多人在使用手机时&#xff0c;经常遇到例如屏幕触摸不准、距离感应器不反应、手机按键失灵等类似问题&#xff0c;有的情况是系统故障&#xff0c;但大多数是属于硬件故障&#xff0c;那如何判定呢&#xff1f;其实很多安卓手机自带的有硬件检测工具&#xff0c;下面就以小米…

String StringBuilder StringBuffer三者之间的区别~~~

String StringBuilder StringBuffer三者之间的区别~~~String是字符串常量&#xff0c;它是典型的immutable类&#xff0c;被声明final class&#xff0c;所有的属性都是final&#xff0c;由于它的不变性&#xff0c;类似拼接&#xff0c;剪裁字符串等操作&#xff0c;都会产生…

qq传输文件的软件测试点,超强新功能 QQ传文件夹测试版抢先试用

QQ传文件夹测试版试用(1)中关村在线软件事业部消息 腾讯体验中心今日(7月29日)发布了一个新的体验项目“QQ2009传文件夹测试版”。QQ的传文件功能&#xff0c;相信多数用户都使用过。在之前的版本中&#xff0c;很多用户反馈希望增加传文件夹的功能&#xff0c;方便多文件的传输…

String String.valueof() toString三者之间的区别~

String String.valueof() toString三者之间的区别~&#xff08;String &#xff09;强制类型转换&#xff0c;使用这种方法时&#xff0c;需要注意的是类型必须能转成String类型。因此最好用instanceof做个类型检查&#xff0c;以判断是否可以转换。否则容易抛出CalssCastExc…

SpringBoot学习——@Autowired自动注入报:could not be found问题的理解和解决方案

微服务应用程序中&#xff0c;我们会通过Java后台的方式发送http请求并调用其他注册在Spring Cloud Eureka server上的微服务&#xff0c;之前我们可能会手动封装一个Http发送请求类&#xff0c;然后通过其中的sendGet或者sendPost方法借由java IO的形式发送出去。 但是&#…

黑色玫瑰服务器延迟高,LOL:含金量最高的服务器,黑色玫瑰垫底,最后一个钻石起步!...

原标题&#xff1a;LOL&#xff1a;含金量最高的服务器&#xff0c;黑色玫瑰垫底&#xff0c;最后一个钻石起步&#xff01;黑色玫瑰。它是英雄联盟中非常特别的一个大区。这个大区是中间位置&#xff0c;电十&#xff0c;却是比电二高手还要多的地方。因为这个大区小姐姐特别的…