1.前言

实际业务中,大多业务类只需要一个对象就能完成所有工作,另外再创建其他对象就显得浪费内存空间了,例如web开发中的servlet,这时便要用到单例模式,就如其名一样,此模式使某个类只能生成唯一的一个对象。单例模式,根据单例对象创建的时机不同,可以分为懒汉模式和饿汉模式。

2.饿汉(单例)模式

饿汉模式,是在一开始的类加载完成后,便创建一个实例对象。此时不管项目中会不会用到这个对象,这个对象都会被创建,它会在内存中占用一定的空间。如果实在需要此对象,需要调用它的某些方法进行业务处理,那就用getInstance()直接从内存中取出这个对象的引用。

  1. public class HungrySingleton {
  2. //声明一个当前类的变量为类变量,方便静态方法访问,保证还存在一个当前类的对象
  3. private static HungrySingleton singleInstance = new HungrySingleton();
  4. private String description; //其他属性
  5.  
  6. /*
  7. * 私有化唯一的默认构造方法,使其不能在外部(其他类中)通过构造方法创建实例对象
  8. */
  9. private HungrySingleton() {}
  10.  
  11. /**
  12. * 返回此类的唯一对象
  13. * @return
  14. */
  15. /*
  16. * 因构造器被私有化,外部不能直接获得实例对象,也就不能调用实例方法获得对象的引用
  17. * (这也就陷入了死循环。外部起初没有持有singleInstance的引用,现实需求又想通过此对象来调用方法获得它的引用,
  18. * 另外若外部本来持有它的引用,就根本用不着调用方法来获得它的引用。)
  19. * 因此只能将此方法声明为与对象无关的静态方法,静态方法又不能直接访问成员变量,singleInstance只能声明为静态的类变量
  20. */
  21. public static HungrySingleton getInstance() {
  22. return singleInstance;
  23. }
  24.  
  25. }

  

3.懒汉(单例)模式

懒汉模式,在类初始化的时候只会将singleInstance 字段(类变量)初始为空null,并不会将其实例化;只有在确实需要这个对象,需要用这个对象的方法进行业务处理时,才对其初始化。这里用到了"对象初始化延迟”的理念,这样做可以尽可能做到减少内存开锁,实现按需分配内存。而在多线程环境中,多个线程访问同一个全局变量可能会出现线程安全问题,即某个线程访问的这个变量值可能是不安全的,可能是“已过期”的值。就如一个线程刚刚通过getInstance()方法改变了singleInstance 的状态,singleInstance 已经被实例化了,而且并未退出方法,全局变量singleInstance 还未更新,但此时另一个线程也进入了getInstance()方法,却发现singleInstance 并未实例化,这个方法会再去实例化一个对象。这种情况下,单例模式就不能保证实现真的“单例”,因此方法或代码块需要设置同步。参考网上的帖子,以双重校验锁的形式在保证线程安全的同时,降低同步的性能损耗。另外在静态方法中getInstance() 中要访问singleInstance,而singleInstance是类变量,那么要保证线程安全就必须给类加锁(Class元类中默认包含类锁,对象中也默认包含对象锁),而不是给对象加锁。

  1. public class LazySingleton {
  2. // 用"volatile"关键字声明,防止编译优化,改变内存分配顺序
  3. private static volatile LazySingleton singleInstance = null;
  4. // 元类锁
  5. private static final Class<?> CLASS_LOCK = LazySingleton.class;
  6.  
  7. //对象计数器
  8. private static int instanceCount=0;
  9.  
  10. private LazySingleton() {
  11. }
  12.  
  13. /**
  14. * 返回此类的唯一对象
  15. * @return
  16. */
  17. public static LazySingleton getInstance() {
  18. /*
  19. * 设置同步块,保证在多线程条件下,也只有一个对象(单线程下,不需要同步)
  20. *
  21. * 只有检测到singleInstance未被实例化时,才设置同步块。
  22. * 如果检测到singleInstance已被实例化,则直接返回其引用。
  23. * 这种双重同步验证锁,比给整个方法中的代码设为同步块或将方法声明为同步方法,性能要高得多。
  24. * 因为双重同步验证,只会同步一次,其另外两种思路一直都需要同步。
  25. *
  26. */
  27. if (singleInstance == null) {
  28. synchronized (CLASS_LOCK) {
  29. if (singleInstance == null) {
  30. singleInstance = new LazySingleton();
  31. instanceCount++;
  32. }
  33. }
  34. }
  35. return singleInstance;
  36. }
  37. }

  

4.总结

两种单例模式各有其好处,各有其适用场景。饿汉模式,不管实际需不需要这个单例对象,它都会被创建在内存中,而且会放在静态区,而静态区的内存很难被垃圾回收器(GC)所回收,这样就容易造成内存浪费。如果需要这个实例,则可以直接到内存中取这个对象的引用,而不需要再去创建它了,因此就少去了再去创建对象的时间开销。

而懒汉模式,做到了“按需配置”,如果不需要这个单例对象,此对象便不会被创建实例化,内存中也就不会为此对象分配内存空间,这样就减少了没必要的内存消耗。但如果需要这个实例,java虚拟机再去主动实例化它,然后再返回实例化后的对象引用。在这里第一次getInstance()时获单例对象时,所花费的时间要比饿汉模式要多一些,因为多了一个实例化对象的操作。

简单来说,饿汉模式是“以空间换时间”,而懒汉模式是“以时间换空间”。

漫谈设计模式(二):单例(Singleton)模式的更多相关文章

  1. 设计模式C++描述----01.单例(Singleton)模式

    一.概念 单例模式:其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享. class CSingleton { //公有的静态方法,来获取该实例 public: s ...

  2. Android与设计模式——单例(Singleton)模式

    概念: java中单例模式是一种常见的设计模式.单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类仅仅能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...

  3. 【Java学习笔记之三十】详解Java单例(Singleton)模式

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  4. JAVA中实现单例(Singleton)模式的八种方式

    单例模式 单例模式,是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例.即一个类只有一个对象实例. 基本的实现思路 单 ...

  5. 设计一个线程安全的单例(Singleton)模式

    在设计单例模式的时候.尽管非常easy设计出符合单例模式原则的类类型,可是考虑到垃圾回收机制以及线程安全性.须要我们思考的很多其它.有些设计尽管能够勉强满足项目要求,可是在进行多线程设计的时候.不考虑 ...

  6. 设计模式:单例(Sigleton)模式

    题目:设计一个类,我们只能生成该类的一个实例. 只能生成一个实例的类是实现了Singleton(单例)模式的类型. 相关知识: 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象 ...

  7. 单例Singleton模式的两种实现方法

    在设计模式中,有一种叫Singleton模式的,用它可以实现一次只运行一个实例.就是说在程序运行期间,某个类只能有一个实例在运行.这种模式用途比较广泛,会经常用到,下面是Singleton模式的两种实 ...

  8. Java设计模式之 — 单例(Singleton)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8860649 写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上 ...

  9. JAVA设计模式之单例(singleton)

    一.饿汉式 /** * 饿汉式 */public class Singleton01 { private static final Singleton01 instance = new Singlet ...

随机推荐

  1. HDOJ 1722--Cake(切蛋糕问题)

    一次生日Party可能有p人或者q人参加,现准备有一个大蛋糕.问最少要将蛋糕切成多少块(每块大小不一定相等),才能使p人或者q人出席的任何一种情况,都能平均将蛋糕分食. Input 每行有两个数p和q ...

  2. Elasticsearch 更新文档

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  3. python练习(一)----打印九九乘法表

    打印九九乘法表 ,): ,i+): print("{0} x {1} = {2} \t".format(j,i,i*j),end='') //print默认end=‘\n’, pr ...

  4. location - 修改url后 - 重新加载

    window.location.href = window.location.pathname + search;

  5. 【pwnable.kr】fb

    这是pwnable.kr的签到题,记录pwn入门到放弃的第一篇. ssh fd@pwnable.kr -p2222 (pw:guest) 题目很简单,登录上了ssh后,发现了3个文件:fd,fd.c, ...

  6. Express ~ 获取表单 get 和 post 提交方式传送参数的对比

      一,获取 get 提交的参数   var id = req.query.id || ''   二,获取 post 提交的参数 var name = req.body.name || ''

  7. 自定义环形进度条RoundProgressBar

    一.效果图: Canvas画圆环说明: 圆环宽度不必在意,只是画笔宽度设置后达到的效果. 二.实现步骤 1.自定义View-RoundProgressBar 2.设置属性resources(decle ...

  8. hibernate 插入date值到postgresql,丢失时分秒

    用hibernate插入java.util.Date数据时发现 时分秒 会丢失.如 2014-05-30 15:59:16.921 在postgresql数据库中显示2014-05-30 00:00: ...

  9. 高分Essay写作需注意以下几个细节

    目前为止,已经有很多同学问我这个了,所以这次专门来讨论下这个问题.英语essay能达到预期分数吗,答案是肯定的,甚至会超出预期,但是必须注意到几方面,越是细节问题,越容易导致丢分,所以请重视-! 一. ...

  10. Adaboost的python实现

    不要总是掉包欧,真的丢人啊,一起码起来! '''函数的功能:单层决策树分类函数参数说明: xMat:数据矩阵 i:第i列,第几个特征 Q:阈值返回分类结果: re'''import numpy as ...