<%@ page contentType="text/html; charset=gb2312"%> 单例模式陷阱
网站公告:   ◆北天JAVA技术网热情为java爱好者服务,本网内容包括JAVA(JSP、servlet、EJB、webservice、j2ee、javabean、应用服务器、JavaScript),数据库(MYSQL、SQL Server、Sybase、Oracle、DB2、数据库综合知识),设计研究(设计模式、Struts、Spring、Hibernate、设计框架、设计综合知识),WEB2.0新技术(主要介绍AJAX),以及各种技术的入门、实例、例子等等,欢迎各位多来坐坐!◆  诚邀各位JAVA爱好者加盟!◆  本网站内容丰富,更新快,保证每周20篇以上!  
加入收藏
设为首页
联系站长
承接项目
  相关资源:网站首页 | 免费培训学院 | 技术论坛 | JAVA聊天室 | 作家专栏 | 开发工具 | 认证考试 | 会员俱乐部
  JAVA技术初学者园地 | jsp与servlet | javascript | Java源代码 | EJB | web service | 应用服务器 | JAVA综合知识
  设计研究设计模式 | 设计框架 | Struts | Spring | Hibernate | 开源项目 | 面向对象设计 | 设计综合知识
  数 据 库MYSQL | SQL Server | Sybase | Oracle | DB2 | Informix | Access | 数据库综合知识
  其他资源:AJAX新技术 | 网站开发 | ERP软件 | OA办公软件 | 商业智能BI | 开发综合知识 | 承接项目 | 项目试用

 
 
单例模式陷阱
     发布者: 发布时间:2006-10-20

     下面我谈谈我对单例模式的看法。逐一分析单例模式的陷阱,帮助大家正确使用单例模式。                                         

(1) 陷阱一:调用函数的性能瓶颈
      在c++中,单例只有一种实现方式——LazySingleton, 实现如下(本文全部使用java代码):

public class LazySingleton {
  private static LazySingleton m_instance = null ;
  private LazySingleton(){};

  synchronized public static LazySingleton getInstance(){
   if(m_instance!=null)
     m_instance=new LazySingleton();
     return m_instance;
   }
}

   
     LazySingleton将对象的初始化推迟到调用的时候。并且为了防止多线程环境下产生多个实例,使用synchronized
关键字保证函数getInstance调用的线程安全。synchronized关键字的存在保证了只会产生一个对象,但也成了多
线程环境下的性能瓶颈。一个多线程的程序,到了这里却要排队等候成了一个单线程式的执行流程,这在高并发环
境下是不可容忍的。而c++中可以使用双重检查机制将这种性能问题仅仅限制在第一次构造对象的时候,而java中
不可以使用双重检查机制。

但是java可以实现EagerSingleton,实现如下:

public class EagerSingleton {
    private static EagerSingleton m_instance = new EagerSingleton();
    private EagerSingleton(){};
    public static agerSingleton getInstance(){
      return m_instance;
    }
   }

与LazySingleton相比,EagerSingleton将对象的初始化放到了类加载的时候。这样就避免了synchronized关键字的性能瓶颈。


(2)陷阱二:访问互斥共享资源
EagerSingleton中访问互斥资源也要考虑线程安全问题。下面看一个例子: public class EagerSingleton{ private static EagerSingleton m_instance=new EagerSingleton(); private HashMap map=new HashMap(); private EagerSingleton(){};
public static agerSingleton getInstance(){ return m_instance; }
public void refreshMap(Object key){ synchronized(map){ if(!map.contains(key)) map.put(key,value);//value为此时的实时数据 } } }

因为该类是单例,可能多线程并发访问map,map非线程安全,需要加线程安全关键字,否则就掉入了访问互斥资源的陷阱。
(3)陷阱三:非法逻辑陷阱
这种情况一般是滥用单例模式造成的,下面考虑一种滥用单例的情况。下面的代码的作用是getValueByName后,马上printValue
即完成操作流程。
public class EagerSingleton{ private static EagerSingleton m_instance=new EagerSingleton(); private String value=null; private EagerSingleton(){};
public static agerSingleton getInstance(){ return m_instance; } synchronized public void getValueByName(String name){ value=getByNameFromDateBase(name); } public viod printValue(){ System.out.println(this.vaue); } }
     该类含有一私有属性value,在多线程环境下不能保证value值的合理逻辑,一线程getValueByName后,马上printValue,
也有可能value的值已经被其他线程修改。这种情况就属于单例模式的滥用,该类根本不适合做成单例。 消除非法逻辑的陷阱,可以通过将该类重构为纯粹的行为类完成。重构后的代码如下: public class EagerSingleton{ private static EagerSingleton m_instance=new EagerSingleton(); private EagerSingleton(){}; public static agerSingleton getInstance(){ return m_instance; } private String getValueByName(String name){ return getByNameFromDateBase(name); } public viod printName(String name){ String value=getValueByName(String name); System.out.println(value); } } 通过调用printName(String name)直接完成操作流程,将其中的私有属性处理成过程式的参数传递,
将该类修改成纯粹的行为类。含有私有属性并且含有对它赋值操作的类并非都会调入该陷阱,构造函数
里进行对私有属性赋值不会引起非法逻辑,如下代码 public class EagerSingleton{ private static EagerSingleton m_instance=new EagerSingleton(); private HashMap map==new HashMap(); private EagerSingleton(){ map.put(key,value);//value为此时的实时数据 } public static agerSingleton getInstance(){ return m_instance; } } 构造函数里不必要加线程安全关键字也可以保证线程安全,因为类加载器是线程安全的,
EagerSingleton只会在类加载的时候实例化一次,这样不会出现单例模式的线程不安全,也不会造成非法逻辑。
(4)陷阱四:单例陷阱的传递 当含有对象作为单例类的私有属性时,陷阱不仅会出现在该类本身,还会传递到私有对象所在的类中。看如下代码: public class EagerSingleton{ private static EagerSingleton m_instance=new EagerSingleton(); private NewClass newClass=nll; private EagerSingleton(){ newClass=new NewClass(); }; public static agerSingleton getInstance(){ return m_instance; } public viod printName(String name){ String value=newClass.operationByNameAndReturnValue(String name); System.out.println(value); } }

        乍一看,代码中除了构造函数对私有属性进行了初始化操作,其他地方没有对私有属性的赋值,
不会引起非法逻辑陷阱。其实这个赋值操作可能隐含在newClass.operationByNameAndReturnValue(String name)操作,
只有保证了NewClass的operationByNameAndReturnValue操作不会对它的私有属性赋值操作,才能保证真正的合理逻辑。
同样,只有保证NewClass的operationByNameAndReturnValue操作没有掉入访问互斥资源陷阱,才能真正
保证EagerSingleton没有掉入该陷阱。
消除该陷阱的方法:
(1)类方法的名称要合理,比如纯粹的行为方法名:interprete,excute,operation之类
的方法中就不该含有对私有属性直接或者间接的赋值操作,每个方法的责任要明确。

(2)单例类中尽量不要含有非单例类的实例作为私有属性(容器类除外),一定要有类的实例作为私有属性的时候,
重新审视这个作为私有属性的类,是不是也应该设计成单例类;或者保证对它的初始化赋值限制在构造函数内。
(转载文章请保留出处:北天JAVA技术网(www.java114.com))
 
更多精彩文章:
Dom4j学习笔记
三重DES加密程序
java多态性教学实例
jsp处理表单的一些经验分享
Java反射经典实例
使用Java中的Date和Calendar类
 
最近评论:
        
冰封的往事!
wow power leveling,wow gold,wow power leveling,wow gold max(5746)
        
飞舞的传奇!
传世私服,传世私服.传奇世界私服传奇世界私服,传世私服传世私服, 传奇世界私服传奇世界私服.传奇私服传奇私服. max(3516)
        
标 题:   
内 容:   
 
                                  
 
免责声明:该文章由网友发表,如果对您造成侵权,请联系站长

首页 - 承接项目 - 网站地图 - 联系我们 -
版权所有北天JAVA技术工作室 ICP证号:粤ICP备06079815号