单例模式(Singleton)

单例对象(Singleton)是一种经常使用的设计模式。

在Java应用中,单例对象能保证在一个JVM中,该对象仅仅有一个实例存在。单例模式也分三种:懒汉式单例、饿汉式单例、登记式单例。

单例模式有几个长处:

  • 1、某些类创建比較频繁,对于一些大型的对象,这是一笔非常大的系统开销。
  • 2、省去了new操作符,减少了系统内存的使用频率,减轻GC压力。
  • 3、有些类如交易所的核心交易引擎。控制着交易流程,假设该类能够创建多个的话,系统全然乱了。(比方一个军队出现了多个司令员同一时候指挥,肯定会乱成一团),所以仅仅有使用单例模式,才干保证核心交易server独立控制整个流程。

首先我们写一个简单的单例类(懒汉式):

  1. public class Singleton {
  2. /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟载入 */
  3. private static Singleton instance = null;
  4. /* 私有构造方法,防止被实例化 */
  5. private Singleton() {
  6. }
  7. /* 静态project方法。创建实例 */
  8. public static Singleton getInstance() {
  9. if (instance == null) {
  10. instance = new Singleton();
  11. }
  12. return instance;
  13. }
  14. /* 假设该对象被用于序列化。能够保证对象在序列化前后保持一致 */
  15. public Object readResolve() {
  16. return instance;
  17. }
  18. }

这个类能够满足基本要求,可是。像这样毫无线程安全保护的类。假设我们把它放入多线程的环境下,肯定就会出现故障了,怎样解决?我们首先会想到对getInstance方法加 synchronized keyword,例如以下:

  1. public static synchronized Singleton getInstance() {
  2. if (instance == null) {
  3. instance = new Singleton();
  4. }
  5. return instance;
  6. }

可是。synchronizedkeyword锁住的是这个对象,这种使用方法,在性能上会有所下降,由于每次调用getInstance(),都要对对象上锁。事实上,仅仅有在第一次创建对象的时候须要加锁,之后就不须要了,所以,这个地方须要改进。

我们改成以下这个:

  1. public static Singleton getInstance() {
  2. if (instance == null) {
  3. synchronized (instance) {
  4. if (instance == null) {
  5. instance = new Singleton();
  6. }
  7. }
  8. }
  9. return instance;
  10. }

似乎攻克了之前提到的问题,将synchronizedkeyword加在了内部。也就是说当调用的时候是不须要加锁的,仅仅有在instance为null,并创建对象的时候才须要加锁,性能有一定的提升。可是。这种情况,还是有可能有问题的,看以下的情况:在Java指令中创建对象和赋值操作是分开进行的。也就是说instance = new Singleton();语句是分两步执行的。

可是JVM并不保证这两个操作的先后顺序。也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了。我们以A、B两个线程为例:

a)A、B线程同一时候进入了第一个if推断

b)A首先进入synchronized块,由于instance为null,所以它执行 instance = new Singleton();

c)由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有開始初始化这个实例),然后A离开了synchronized块。

d)B进入synchronized块,由于instance此时不是null,因此它立即离开了synchronized块并将结果返回给调用该方法的程序。

e)此时B线程打算使用Singleton实例,却发现它没有被初始化,于是发生错误了。

所以程序还是有可能发生错误,事实上程序在执行过程是非常复杂的,从这点我们就能够看出,尤其是在写多线程环境下的程序更有难度。有挑战性。

我们对该程序做进一步优化(饿汉式):

  1. private static class SingletonFactory{
  2. private static Singleton instance = new Singleton();
  3. }
  4. public static Singleton getInstance(){
  5. return SingletonFactory.instance;
  6. }

实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被载入的时候。这个类的载入过程是线程相互排斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance仅仅被创建一次。并且会保证把赋值给instance的内存初始化完成。这样我们就不用操心上面的问题。

同一时候该方法也仅仅会在第一次调用的时候使用相互排斥机制,这样就攻克了低性能问题。

这样我们临时总结一个完美的单例模式:

  1. public class Singleton {
  2. /* 私有构造方法。防止被实例化 */
  3. private Singleton() {
  4. }
  5. /* 此处使用一个内部类来维护单例 */
  6. private static class SingletonFactory {
  7. private static Singleton instance = new Singleton();
  8. }
  9. /* 获取实例 */
  10. public static Singleton getInstance() {
  11. return SingletonFactory.instance;
  12. }
  13. /* 假设该对象被用于序列化,能够保证对象在序列化前后保持一致 */
  14. public Object readResolve() {
  15. return getInstance();
  16. }
  17. }

事实上说它完美也不一定,假设在构造函数中抛出异常,实例将永远得不到创建,也会出错。

所以说,十分完美的东西是没有的,我们仅仅能依据实际情况,选择最适合自己应用场景的实现方法。

也有人这样实现:由于我们仅仅须要在创建类的时候进行同步,所以仅仅要将创建和getInstance()分开。单独为创建加synchronizedkeyword,也是能够的:

  1. public class SingletonTest {
  2. private static SingletonTest instance = null;
  3. private SingletonTest() {
  4. }
  5. private static synchronized void syncInit() {
  6. if (instance == null) {
  7. instance = new SingletonTest();
  8. }
  9. }
  10. public static SingletonTest getInstance() {
  11. if (instance == null) {
  12. syncInit();
  13. }
  14. return instance;
  15. }
  16. }

考虑性能的话,整个程序仅仅需创建一次实例。所以性能也不会有什么影响。

补充:採用"影子实例"的办法为单例对象的属性同步更新

  1. public class SingletonTest {
  2. private static SingletonTest instance = null;
  3. private Vector properties = null;
  4. public Vector getProperties() {
  5. return properties;
  6. }
  7. private SingletonTest() {
  8. }
  9. private static synchronized void syncInit() {
  10. if (instance == null) {
  11. instance = new SingletonTest();
  12. }
  13. }
  14. public static SingletonTest getInstance() {
  15. if (instance == null) {
  16. syncInit();
  17. }
  18. return instance;
  19. }
  20. public void updateProperties() {
  21. SingletonTest shadow = new SingletonTest();
  22. properties = shadow.getProperties();
  23. }
  24. }

通过单例模式的学习告诉我们:

1、单例模式理解起来简单,可是详细实现起来还是有一定的难度。

2、synchronizedkeyword锁定的是对象,在用的时候,一定要在恰当的地方使用(注意须要使用锁的对象和过程,可能有的时候并非整个对象及整个过程都须要锁)。

到此,单例模式基本就介绍完了,事实上採用类的静态方法,实现单例模式的效果,也是可行的。此处二者有何不同呢?

  • 首先。静态类不能实现接口。

    (从类的角度说是能够的。可是那样就破坏了静态了。

    由于接口中不同意有static修饰的方法。所以即使实现了也是非静态的)

  • 其次。单例能够被延迟初始化,静态类一般在第一次载入是初始化。

    之所以延迟载入。是由于有些类比較庞大,所以延迟载入有助于提升性能。

  • 再次,单例类能够被继承。他的方法能够被覆写。

    可是静态类内部方法都是static。无法被覆写。

  • 最后,单例类比較灵活,毕竟从实现上仅仅是一个普通的Java类,仅仅要满足单例的基本需求。你能够在里面随心所欲的实现一些其他功能,可是静态类不行。

从上面这些概括中,基本能够看出二者的差别,可是,从还有一方面讲,上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有非常大的关联。仅仅是我们考虑问题的层面不同罢了。

两种思想的结合,才干造就出完美的解决方式,就像HashMap採用数组+链表来实现一样。事实上生活中非常多事情都是这样,单用不同的方法来处理问题,总是有长处也有缺点,最完美的方法是,结合各个方法的长处,才干最好的解决这个问题。

拓展:

什么是线程安全?假设你的代码所在的进程中有多个线程在同一时候执行,而这些线程可能会同一时候执行这段代码。假设每次执行结果和单线程执行的结果是一样的,并且其他的变量的值也和预期的是一样的,就是线程安全的。

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

java设计模式学习 ----- 单例模式(Singleton)的更多相关文章

  1. Java设计模式之单例模式 - Singleton

    用来创建独一无二的,是能有一个实例的对象的入场券.告诉你一个好消息,单例模式的类图可以说是所有模式的类图中最简单的,事实上,它的类图上只有一个类!但是,可不要兴奋过头,尽管从类设计的视角来说很简单,但 ...

  2. java设计模式之 单例模式 Singleton

    static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...

  3. Java 设计模式(三)-单例模式(Singleton Pattern)

    1     概念定义 1.1   定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2   类型 创建类模式 1.3   难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...

  4. java设计模式学习-单例模式

    java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供.”单例模式可以保证一个应用中有且只有一个实例,避免了资源的浪费和多个实例多次调用导致出错. 单例模式有以下特点: 1.单 ...

  5. Java 设计模式之单例模式(一)

    原文地址:Java 设计模式之单例模式(一) 博客地址:http://www.extlight.com 一.背景 没有太多原由,纯粹是记录和总结自己从业以来经历和学习的点点滴滴. 本篇内容为 Java ...

  6. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  7. java 设计模式之单例模式

    -------Success is getting what you want, happiness is wanting what you get. java设计模式之单例模式(Singleton) ...

  8. Java设计模式学习资源汇总

    本文记录了Java设计模式学习书籍.教程资源.此分享会持续更新: 1. 设计模式书籍 在豆瓣上搜索了一把,发现设计模式贯穿了人类生活的方方面面.还是回到Java与程序设计来吧. 打算先归类,再浏览,从 ...

  9. 设计模式(4) -- 单例模式(Singleton)

    设计模式(4)  -- 单例模式(Singleton) 试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说 ...

随机推荐

  1. 服务器搭建域控与SQL Server的AlwaysOn环境过程(二) 搭建客户端节点 服务器

    1. 修改客户端服务器的计算机名,重启后,如果服务器属于克隆服务器,需要修改服务器SID,如果没有则调过这一步. 命令行方式:启动Windows2008进入系统后,打开“CMD窗口”并进入到" ...

  2. 一:1.2【print&input与变量和运算符】

    [路径] 绝对路径:从根目录开始链接的路径  --->cd C:\Windows\Boot\DVD\EFI\en-US 相对路径:不从根目录开始链接的路径 ----> cd Boot\DV ...

  3. 总结使人进步,可视化界面GUI应用开发总结:Android、iOS、Web、Swing、Windows开发等

    可视化界面的软件,是21世纪最主流的应用类型了,黑屏控制台的不适合普通用户.   2004年左右的时候,作为普通网民,接触的自然是可视化,准确是Windows那一套.   那个时候,Microsoft ...

  4. Mysql学习总结(20)——MySQL数据库优化的最佳实践

    1)谨慎而有效地使用索引 选择合理的索引(前缀性及可选性).删除没有用的索引. 2)使用规范化,但不要使用过头 规范化(至少是第三范式)是一个易于理解且标准的方法.然而,在有些情况下,你可能希望违反这 ...

  5. C++模板遇到iterator时候遇到的问题和解决方法

    今天开发的时候,发现用模板的时候,再加上iterator,会报错,如下: std::map<T, S>::iterator find_iter = mp.find(key); 编译会报错: ...

  6. ios面试题1

    iOS面试题   1.写一个NSString类的实现 + (id)initWithCString:(c*****t char *)nullTerminatedCString encoding:(NSS ...

  7. 公布Qt Widgets桌面应用程序的方法

    公布Qt Widgets桌面应用程序的方法 Qt是一款优秀的跨平台开发框架,它能够在桌面.移动平台以及嵌入式平台上执行.眼下Qt 5介绍程序公布的文章帖子比較少.大家又很想要知道怎样公布Qt应用程序, ...

  8. vim水平摆放全部窗体的三个方法

    Method1: map <F7> <ESC>:windo exe "normal \<foobar<C-W>C-W>K"<C ...

  9. JavaScript中函数作为另一个函数的参数的时候它存在于哪个作用域

    一直对函数作为参数被传递进另外一个函数理解的不是很清除.先看下这段代码吧: function test(fn){ var bar = 1; fn(); } var bar = 99; test(fun ...

  10. angular route 与 django urls 冲突怎么解决?

    app.js var app = angular.module('app', [ 'ngResource', 'ngRoute', // 'ui.bootstrap', // 'ngResource' ...