Java Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。
我们在浏览BBS、SNS网站的时候,常常会看到“当前在线人数”这样的一项内容。对于这样的一项功能,我们通常的做法是把当前的在线人数存放到一个内存、文件或者数据库中,每次用户登录的时候,就会马上从内存、文件或者数据库中取出,在其基础上加1后,作为当前的在线人数进行显示,然后再把它保存回内存、文件或者数据库里,这样后续登录的用户看到的就是更新后的当前在线人数;同样的道理,当用户退出后,当前在线人数进行减1的工作。所以,对于这样的一个需求,我们按照面向对象的设计思想,可以把它抽象为“在线计数器”这样一个对象。
网站代码中凡是用到计数器的地方,只要new一个计数器对象,然后就可以获取、保存、增加或者减少在线人数的数量。不过,我们的代码实际的使用效果并不好。假如有多个用户同时登录,那么在这个时刻,通过计数器取到的在线人数是相同的,于是他们使用各自的计数器加1后存入文件或者数据库。这样操作后续登陆的用户得到的在线人数,与实际的在线人数并不一致。所以,把这个计数器设计为一个全局对象,所有人都共用同一份数据,就可以避免类似的问题,这就是我们所说的单例模式的其中的一种应用。
引用设计模式之禅里面的例子:
第一种方式:
package com.cbf4life.singleton1; /** * @author cbf4Life cbf4life@126.com * I'm glad to share my knowledge with you all. * 中国的历史上一般都是一个朝代一个皇帝,有两个皇帝的话,必然要PK出一个皇帝出来 */ public class Emperor { private static Emperor emperor = null; //定义一个皇帝放在那里,然后给这个皇帝名字 private Emperor(){ //世俗和道德约束你,目的就是不让你产生第二个皇帝 } public static Emperor getInstance(){ 第 12 页 您的设计模式 if(emperor == null){ //如果皇帝还没有定义,那就定一个 emperor = new Emperor(); } return emperor; } //皇帝叫什么名字呀 public static void emperorInfo(){ System.out.println("我就是皇帝某某某...."); } }
但是一种方式有一个缺陷,
假如现在有两个线程A和线程B,线程A执行到 this.singletonPattern = new SingletonPattern(),正在申请内存分配,可能需要0.001微秒,就在这0.001微秒之内,线程B执行到if(this.singletonPattern == null),你说这个时候这个判断条件是true还是false?是true,那然后呢?线程B也往下走,于是乎就在内存中就有两个SingletonPattern的实例了,看看是不是出问题了?如果你这个单例是去拿一个序列号或者创建一个信号资源的时候,会怎么样?业务逻辑混乱!数据一致性校验失败!最重要的是你从代码上还看不出什么问题,这才是最要命的!因为这种情况基本上你是重现不了的,不寒而栗吧,那怎么修改?有很多种方案,我就说一种,能简单的、彻底解决问题的方案
第二种
package com.cbf4life.singleton3; /** * @author cbf4Life cbf4life@126.com * I'm glad to share my knowledge with you all. * 通用单例模式 */ @SuppressWarnings("all") public class SingletonPattern { private static final SingletonPattern singletonPattern= new SingletonPattern(); //限制住不能直接产生一个实例 private SingletonPattern(){ } public synchronized static SingletonPattern getInstance(){ return singletonPattern; } }