摘要:这种情况带来的后果是程序两次创建了对象,这并不符合我们对单例模式的定义。实现的三个特性自由序列化线程安全保证单例。其次,有且仅有的构造器,防止外部的额外构造,这恰好与单例模式吻合。
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。当系统需要某个类只能有一个实例时,就可以采用单例模式。
保证单例模式仅有一个实例的核心思想是构造方法私有化,即不允许外部调用该类的构造方法。基于此思想,主要有以下两种实现方式:
直接实例化直接实例化这种方式也称作“饿汉式”,它直接定义了静态成员变量 s,并通过 new Singleton() 完成了初始化,之后不再变化,是线程安全的。
这种方式也存在一定的资源浪费,当没有使用 Singleton 对象时,程序依然会创建 Singleton 对象。
public class Singleton { private Singleton() {} private static final Singleton s = new Singleton(); public static Singleton getInstance() { return s; } }延迟实例化
既然直接实例化浪费资源,那么我们是否可以考虑,在程序需要该对象的时候才创建它呢?当然可以!
与直接实例化稍不同,单例成员变量 s 初始为 null,它在方法 getInstance() 内部完成延迟实例化,并返回单例对象。
public class Singleton { private Singleton() {} private static Singleton s = null; public static Singleton getInstance() { if (s == null) { s = new Singleton(); } return s; } }
这种方式存在线程安全问题。例如,假设两个线程调用 getInstance() 方法,线程 1 执行完 if(s == null),条件成立,在执行实例化语句 s = new Singleton() 之前,线程 2 来了,此时线程 2 执行 if(s == null),依然成立,进入 if 语句体。这种情况带来的后果是:程序两次创建了对象,这并不符合我们对单例模式的定义。
针对这种情况,可以有以下四种解决方法:
完全同步完全同步方法,是在方法上加上 synchronized 同步。当多线程同时访问 getInstance() 方法时,多线程是“串行”的。
public class Singleton { private Singleton() {} private static Singleton s = null; public static synchronized Singleton getInstance() { if (s == null) { s = new Singleton(); } return s; } }
这种方法,多线程每次访问 getInstance() 都必须“串行”运行,效率比较低。
部分同步部分同步方法通过双重锁部分同步机制获得单例对象。因为代码中有两行相同的语句 if(s == null),故而叫做双重锁。第一个 if 语句可并行,当多线程均满足该条件, synchronized 修饰的代码必须串行运行。这样的话,其实只需要在第一次创建对象(通过了第一个 if 判断)的时候进行同步,效率较高。
public class Singleton { private Singleton() {} private volatile static Singleton s = null; public static Singleton getInstance() { if (s == null) { synchronized(Singleton.class) { if (s == null) { s = new Singleton(); } } } return s; } }
注意,volatile关键字是确保当 s 被初始化成 Singleton 实例时,多个线程可以正确处理 s,即内存可见性。
静态内部类通过静态内部类 Inner 来实现单例对象。虚拟机加载应用程序字节码时,单例对象并不会立即创建,当第一次运行 Inner.s 时,单例对象才动态生成。这种实现方式无 synchronized 关键字,提高了效率。
public class Singleton { private Singleton() {} private static class Inner { private static final Singleton s = new Singleton(); } public static Singleton getInstance() { return Inner.s; } }枚举
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。调用的时候只需要 Singleton.INSTANCE 即可。
public enum Singleton { INSTANCE; // var here public int var; // methods here public void otherMethods() { System.out.println("write other methods here..."); } }
enum 实现 Singleton 的三个特性:自由序列化、线程安全、保证单例。
首先, enum 是由 class 实现的,它可以有 member 和 member function。另外,由于 enum 是通过继承 Enum 类实现的,enum 结构不能作为子类继承其他类,但可以用来实现接口。此外 enum 类不能被继承,在反编译中,可以发现该类由 final 修饰。
其次,enum 有且仅有 private 的构造器,防止外部的额外构造,这恰好与单例模式吻合。
而对于序列化和反序列化,因为每一个枚举类型和枚举变量在 JVM 中都是唯一的,即 Java在序列化和反序列化枚举时做了特殊的规定,枚举的 writeObject、readObject、readObjectNoData、writeReplace 和 readResolve 等方法是被编译器禁用的,因此也不存在实现序列化接口后调用readObject 会破坏单例的问题。
(完)
参考资料
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71552.html
摘要:用来指向已创建好的实例构造函数为空注意这里是关键这是我们需要调用的方法把函数也定义为空,这样就大功告成啦。 接上一篇大话PHP设计模式之单例模式 这一篇介绍一下升级版的单例模式,废话不说先上代码 不完美的单例模式 class singleMode { //用来指向已创建好的实例 public static $instance; //判断是...
摘要:博主按每天一个设计模式旨在初步领会设计模式的精髓,目前采用靠这吃饭和纯粹喜欢两种语言实现。单例模式用途如果一个类负责连接数据库的线程池日志记录逻辑等等,此时需要单例模式来保证对象不被重复创建,以达到降低开销的目的。 博主按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript(_靠这吃饭_)和python(_纯粹喜欢_)两种语言实现。诚然,每种设计模式都有多种实...
摘要:博主按每天一个设计模式旨在初步领会设计模式的精髓,目前采用靠这吃饭和纯粹喜欢两种语言实现。单例模式用途如果一个类负责连接数据库的线程池日志记录逻辑等等,此时需要单例模式来保证对象不被重复创建,以达到降低开销的目的。 博主按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript(_靠这吃饭_)和python(_纯粹喜欢_)两种语言实现。诚然,每种设计模式都有多种实...
摘要:上面是简单的单例模式,自己写程序的话够用了,如果想继续延伸,请传送至大话设计模式之单例模式升级版 看了那么多单例的介绍,都是上来就说怎么做,也没见说为什么这么做的。那小的就来说说为什么会有单例这个模式以便更好的帮助初学者真正的理解这个设计模式,如果你是大神,也不妨看完指正一下O(∩_∩)O首先我不得不吐槽一下这个模式名字单例,初学者通过字面很难理解什么是单例,我觉得应该叫唯一模式更贴切...
摘要:最近开展了三次设计模式的公开课,现在来总结一下设计模式在中的应用,这是第一篇创建型模式之单例模式。不过因为不支持多线程所以不需要考虑这个问题了。 最近开展了三次设计模式的公开课,现在来总结一下设计模式在PHP中的应用,这是第一篇创建型模式之单例模式。 一、设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。 设计模式不...
摘要:原文博客地址单例模式系统中被唯一使用,一个类只有一个实例。中的单例模式利用闭包实现了私有变量两者是否相等弱类型,没有私有方法,使用者还是可以直接一个,也会有方法分割线不是单例最简单的单例模式,就是对象。 原文博客地址:https://finget.github.io/2018/11/06/single/ 单例模式 系统中被唯一使用,一个类只有一个实例。实现方法一般是先判断实例是否存在,...
阅读 746·2019-08-30 15:54
阅读 415·2019-08-30 12:51
阅读 2001·2019-08-29 16:28
阅读 2822·2019-08-29 16:10
阅读 2312·2019-08-29 14:21
阅读 384·2019-08-29 14:09
阅读 2104·2019-08-23 16:13
阅读 1222·2019-08-23 13:59