摘要:代码分析构造函数私有化,防止外部直接调用构造函数通过改静态方法获取单例对象这是典型的懒汉式单例方法,低并发的情况下不会出现问题,若系统压力增大,并发量增加将有非常大的可能创建多个实例。
前言
终于到周末了,又玩起了最爱的lol,最近新版本出了一个特别的天赋--偷钱(具体名字想不起来了),配上ez简直是吊炸天,我玩的单排,仅用了不到三十分钟就杀的对面出不了家,正当我看着伤害板沾沾自喜,对面ad说不就是选了个版本最强ad,有什么了不起的...本帅这种具有理想抱负的屌丝当然不会和这群傻叉多说什么,关闭了游戏,就来写这篇文章,构思的过程中想到,lol单排不能选重复英雄的机制不就是今天要讲的单例模式吗!
什么是单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的优缺点
优点: 1. 既然是单例模式,就说明内存中只存在一个实例,当一个对象需要频繁创建销毁时,而创建销毁对象是虚拟机控制的,我们无法直接优化其性能,单例模式的优势的就会体现的很明显。 缺点: 1.单例模式一般是没有接口的,因为它被默认要求自行实例化。 2.单例模式对测试也是不利的,在并行开发环境下,假如我们的单例代码还没有开发好,没有接口也不能通过mock的方式虚拟一个对象。 3.单例模式和上节讲的单一原则有点冲突,因为单例设计模式把必须单例当作一种业务和其他业务逻辑融入到一个类中。使用场景
对象是无状态的,也就是说一个对象和多个对象办的事都是一样的,这样的话,我们就有必要减少内存使用和gc的消耗。
当创建一个对象消耗的资源过于昂贵,比如通过io流读写文件、连接数据库等资源。
代码分析public class EZ { private static EZ ez = null; //构造函数私有化,防止外部直接调用构造函数 private EZ() { } //通过改静态方法获取单例对象 public static EZ getSingletonEz() { if (ez == null) { return new EZ(); } return ez; } }
这是典型的懒汉式单例方法,低并发的情况下不会出现问题,若系统压力增大,并发量增加将有非常大的可能创建多个实例。我们可以自己创建线程池,自己伪造一个并发环境,代码如下:
public class SingletonTest { private static ExecutorService executorService = Executors.newFixedThreadPool(1000); public static void main(String[] args) throws InterruptedException { //给系统足够的时候启动1000个线程 Thread.sleep(5000); final SetezSet = new HashSet<>(); executorService.submit(new Runnable() { @Override public void run() { ezSet.add(EZ.getSingletonEz().toString()); } }); Thread.sleep(5000); //若创建多个对象,size>1 System.out.println(ezSet.size()); } }
这个例子在公司的时候有测试过,大概运行七八次就可以重现一次创建多个对象的现象。无奈现在家中,电脑配置过高,试了20次,竟一次都没有重现。哎~。~,配置高有时也挺不方便的,你们电脑不好的可以试下¬_¬;既然存在并发情况创建多个对象问题,那我们就有必要改善下:
public class BetterEz { private static BetterEz ez = null; //构造函数私有化,防止外部直接调用构造函数 private BetterEz() { } //通过改静态方法获取单例对象 public static BetterEz getSingletonEz() { if (ez == null) { synchronized (BetterEz.class) { if (ez == null) return new BetterEz(); } } return ez; } }
我们通过synchronized控制同一时间只有一个请求可以访问同步代码块,但为什么又要在内部加一层判断呢?假如线程A、B同时请求,A获得锁执行,B等待。当A执行完,B获得锁再去执行,若没有这个判断岂不又重新创建了新对象。但这种典型的double-check的懒汉单例存在了一个可能发生的问题, jvm接收到new指令时,简单分为3步(实际更多,可参考深入理解虚拟机),1分配内存2实例化对象3将内存地址指向引用。java的内存模型并不限制指令的重排序,也就说当执行步骤从1-》2-》3变成1-》3-》2。当线程a访问走到第2步,未完成实例化对象前,线程b访问此对象的返回一个引用,但若是进行其他操作,因为对象并没有实例化,会造成this逃逸的问题。jdk1.5之后对violate进行了重新解释,使用violate会强制保证线程可见性,增加内存屏障,禁止指令优化,从而避免这个问题。或者采用饿汉式创建对象。
上面说的都是懒汉式单例,还有一种是饿汉式单例。懒汉式的优点在于当一次食用才会new对象,饿汉式的优点在于在第一次获取该对象的效率更高。下面我们看一下饿汉式单例:
public class EZ { private static final EZ ez = new EZ(); private EZ() { } public static EZ getSingletonEz() { return ez; } }
当类第一次初始化的时候,就会对静态域赋值。而jvm对于类的初始化进行了严格规定,有且只有5种情况必须对类进行初始化,关于类加载的问题不属于这篇博客的范畴,就不多说了我们只需理解为这是静态变量只会初始化一次,并且这是虚拟机保证的即可。
以上就是我对单例模式的理解,感谢各位的收看
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68041.html
摘要:用来指向已创建好的实例构造函数为空注意这里是关键这是我们需要调用的方法把函数也定义为空,这样就大功告成啦。 接上一篇大话PHP设计模式之单例模式 这一篇介绍一下升级版的单例模式,废话不说先上代码 不完美的单例模式 class singleMode { //用来指向已创建好的实例 public static $instance; //判断是...
摘要:博主按每天一个设计模式旨在初步领会设计模式的精髓,目前采用靠这吃饭和纯粹喜欢两种语言实现。单例模式用途如果一个类负责连接数据库的线程池日志记录逻辑等等,此时需要单例模式来保证对象不被重复创建,以达到降低开销的目的。 博主按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript(_靠这吃饭_)和python(_纯粹喜欢_)两种语言实现。诚然,每种设计模式都有多种实...
摘要:博主按每天一个设计模式旨在初步领会设计模式的精髓,目前采用靠这吃饭和纯粹喜欢两种语言实现。单例模式用途如果一个类负责连接数据库的线程池日志记录逻辑等等,此时需要单例模式来保证对象不被重复创建,以达到降低开销的目的。 博主按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript(_靠这吃饭_)和python(_纯粹喜欢_)两种语言实现。诚然,每种设计模式都有多种实...
摘要:上面是简单的单例模式,自己写程序的话够用了,如果想继续延伸,请传送至大话设计模式之单例模式升级版 看了那么多单例的介绍,都是上来就说怎么做,也没见说为什么这么做的。那小的就来说说为什么会有单例这个模式以便更好的帮助初学者真正的理解这个设计模式,如果你是大神,也不妨看完指正一下O(∩_∩)O首先我不得不吐槽一下这个模式名字单例,初学者通过字面很难理解什么是单例,我觉得应该叫唯一模式更贴切...
摘要:最近开展了三次设计模式的公开课,现在来总结一下设计模式在中的应用,这是第一篇创建型模式之单例模式。不过因为不支持多线程所以不需要考虑这个问题了。 最近开展了三次设计模式的公开课,现在来总结一下设计模式在PHP中的应用,这是第一篇创建型模式之单例模式。 一、设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。 设计模式不...
摘要:原文博客地址单例模式系统中被唯一使用,一个类只有一个实例。中的单例模式利用闭包实现了私有变量两者是否相等弱类型,没有私有方法,使用者还是可以直接一个,也会有方法分割线不是单例最简单的单例模式,就是对象。 原文博客地址:https://finget.github.io/2018/11/06/single/ 单例模式 系统中被唯一使用,一个类只有一个实例。实现方法一般是先判断实例是否存在,...
阅读 2577·2019-08-30 10:53
阅读 3183·2019-08-29 16:20
阅读 2933·2019-08-29 15:35
阅读 1751·2019-08-29 12:24
阅读 2865·2019-08-28 18:19
阅读 1838·2019-08-23 18:07
阅读 2314·2019-08-23 15:31
阅读 1158·2019-08-23 14:05