Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 528|回复: 0

[设计模式学习]单例模式陷阱

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-28 23:57:21 | 显示全部楼层 |阅读模式
    下面我谈谈我对单例模式的看法。逐一分析单例模式的陷阱,帮助大家正确使用单例模式。                                         

    (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;
        }
    }     
       
       
       
       

       
      
      
    1.    
    2.      LazySingleton将对象的初始化推迟到调用的时候。并且为了防止多线程环境下产生多个实例,使用synchronized
    3. 关键字保证函数getInstance调用的线程安全。synchronized关键字的存在保证了只会产生一个对象,但也成了多
    4. 线程环境下的性能瓶颈。一个多线程的程序,到了这里却要排队等候成了一个单线程式的执行流程,这在高并发环
    5. 境下是不可容忍的。而c++中可以使用双重检查机制将这种性能问题仅仅限制在第一次构造对象的时候,而java中
    6. 不可以使用双重检查机制。
    7.      但是java可以实现EagerSingleton,实现如下:
    复制代码
    public class EagerSingleton {
         private static EagerSingleton m_instance = new EagerSingleton();
         private EagerSingleton(){};
         public static agerSingleton getInstance(){
           return m_instance;
         }
        }

    与LazySingleton相比,EagerSingleton将对象的初始化放到了类加载的时候。这样就避免了synchronized关键字的性能瓶颈。
    1. (2)陷阱二:访问互斥共享资源

    2. EagerSingleton中访问互斥资源也要考虑线程安全问题。下面看一个例子:
    3. public class EagerSingleton{
    4.     private static EagerSingleton m_instance=new EagerSingleton();
    5.     private HashMap map=new HashMap();
    6.     private EagerSingleton(){};

    7.     public static agerSingleton getInstance(){
    8.         return m_instance;
    9.     }

    10.     public void refreshMap(Object key){
    11.         synchronized(map){
    12.             if(!map.contains(key))
    13.                 map.put(key,value);//value为此时的实时数据
    14.         }
    15.     }
    16. }
    17. 因为该类是单例,可能多线程并发访问map,map非线程安全,需要加线程安全关键字,否则就掉入了访问互斥资源的陷阱。

    18. (3)陷阱三:非法逻辑陷阱

    19.    这种情况一般是滥用单例模式造成的,下面考虑一种滥用单例的情况。下面的代码的作用是getValueByName后,马上printValue
    20. 即完成操作流程。

    21. public class EagerSingleton{
    22.     private static EagerSingleton m_instance=new EagerSingleton();
    23.     private String value=null;
    24.     private EagerSingleton(){};

    25.     public static agerSingleton getInstance(){
    26.         return m_instance;
    27.     }
    28.     synchronized public void getValueByName(String name){
    29.         value=getByNameFromDateBase(name);
    30.         
    31.     }
    32.     public viod printValue(){
    33.         System.out.println(this.vaue);
    34.     }
    35. }

    36.      该类含有一私有属性value,在多线程环境下不能保证value值的合理逻辑,一线程getValueByName后,马上printValue,
    37. 也有可能value的值已经被其他线程修改。这种情况就属于单例模式的滥用,该类根本不适合做成单例。
    38.         消除非法逻辑的陷阱,可以通过将该类重构为纯粹的行为类完成。重构后的代码如下:
    39. public class EagerSingleton{
    40.     private static EagerSingleton m_instance=new EagerSingleton();
    41.     private EagerSingleton(){};
    42.     public static agerSingleton getInstance(){
    43.         return m_instance;
    44.     }
    45.     private String getValueByName(String name){
    46.         return getByNameFromDateBase(name);
    47.         
    48.     }
    49.     public viod printName(String name){
    50.         String value=getValueByName(String name);
    51.         System.out.println(value);
    52.     }
    53. }
    54. 通过调用printName(String name)直接完成操作流程,将其中的私有属性处理成过程式的参数传递,
    55. 将该类修改成纯粹的行为类。含有私有属性并且含有对它赋值操作的类并非都会调入该陷阱,构造函数
    56. 里进行对私有属性赋值不会引起非法逻辑,如下代码
    57. public class EagerSingleton{
    58.     private static EagerSingleton m_instance=new EagerSingleton();
    59.     private HashMap map==new HashMap();
    60.    
    61.     private EagerSingleton(){
    62.         map.put(key,value);//value为此时的实时数据
    63.     }
    64.     public static agerSingleton getInstance(){
    65.         return m_instance;
    66.     }
    67. }
    68. 构造函数里不必要加线程安全关键字也可以保证线程安全,因为类加载器是线程安全的,
    69. EagerSingleton只会在类加载的时候实例化一次,这样不会出现单例模式的线程不安全,也不会造成非法逻辑。

    70. (4)陷阱四:单例陷阱的传递
    71.         当含有对象作为单例类的私有属性时,陷阱不仅会出现在该类本身,还会传递到私有对象所在的类中。看如下代码:
    72. public class EagerSingleton{
    73.     private static EagerSingleton m_instance=new EagerSingleton();
    74.     private NewClass newClass=nll;
    75.     private EagerSingleton(){
    76.         newClass=new NewClass();
    77.     };
    78.     public static agerSingleton getInstance(){
    79.         return m_instance;
    80.     }
    81.     public viod printName(String name){
    82.         String value=newClass.operationByNameAndReturnValue(String name);
    83.         System.out.println(value);
    84.     }
    85. }
    86.         乍一看,代码中除了构造函数对私有属性进行了初始化操作,其他地方没有对私有属性的赋值,
    87. 不会引起非法逻辑陷阱。其实这个赋值操作可能隐含在newClass.operationByNameAndReturnValue(String name)操作,
    88. 只有保证了NewClass的operationByNameAndReturnValue操作不会对它的私有属性赋值操作,才能保证真正的合理逻辑。
    89. 同样,只有保证NewClass的operationByNameAndReturnValue操作没有掉入访问互斥资源陷阱,才能真正
    90. 保证EagerSingleton没有掉入该陷阱。

    91.         消除该陷阱的方法:
    92. (1)类方法的名称要合理,比如纯粹的行为方法名:interprete,excute,operation之类
    93. 的方法中就不该含有对私有属性直接或者间接的赋值操作,每个方法的责任要明确。
    94. (2)单例类中尽量不要含有非单例类的实例作为私有属性(容器类除外),一定要有类的实例作为私有属性的时候,
    95. 重新审视这个作为私有属性的类,是不是也应该设计成单例类;或者保证对它的初始化赋值限制在构造函数内。

    复制代码

       
         
         
          
          

            
          

            
          
         
       
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-26 01:23 , Processed in 0.374783 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表