`
zmo_xu
  • 浏览: 62222 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
最近访客 更多访客>>
社区版块
存档分类
最新评论

java设计模式全解[1]-单例模式

阅读更多

在网上找了很久都没有找到详细分析java 设计模式的 后来在网上找到个.net的 看了后决定 对C#的代码进行java本地化(借用下这个词,不知道把C#变java叫做什么) 不敢独享 拿来与大家分析,当作搬家到javaeye 后zmo_xu给大家的见面礼吧,还请各位高手不要耻笑区区在下(文章有自己的理解也有网上原文,如果你发现zmo_xu个文章里面设计到了你的文章的版权 敬请致电 zmo2xu@gmail.com 我会及时处理),好了言归正传

第1章 单件模式(Single Pattern)

概述



    Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点。这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任。
从另一个角度来说,Singleton模式其实也是一种职责型模式。因为我们创建了一个对象,这个对象扮演了独一无二的角色,在这个单独的对象实例中,它集中了它所属类的所有权力,同时它也肩负了行使这种权力的职责!


意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

生活中的例子


美国总统的职位是Singleton,美国宪法规定了总统的选举,任期以及继任的顺序。这样,在任何时刻只能由一个现任的总统。无论现任总统的身份为何,其头衔"美利坚合众国总统"是访问这个职位的人的一个全局的访问点。

简单实现:最简单的实现就是在类内声明一个此类的实例并将构造函数私有化(为什么要用private而不用protected来保持可扩展性是因为 当类被继承后影响单例模式) 并向外公开一个getInstance()方法来获取这个实例集体实现 集体实现

java 代码
  1. package unit;   
  2.   
  3. public class Singletnon   
  4. {   
  5.  private static  Singletnon instance;   
  6.     
  7.  private Singletnon()   
  8.  {   
  9.      
  10.  }   
  11.  public static Singletnon getInstance()   
  12.  {   
  13.   if(instance==null)   
  14.   {   
  15.    instance=new Singletnon();   
  16.   }   
  17.   return instance;   
  18.  }   
  19. }   


大家可以看到这里的构造方法

 

是私有的,类外是无法访问的,就是说实现了限制实例化,但是一个类如果没有办法实例化那么这个类将什么也作不了 所以在这个类里面就的提供一个方法来实现实例化 也就是这里的getInstance将在类内对自己实例化并将实例传递出去,好了到此我们已经理解单例模式的实现,那么我们就要考虑安全问题 还有我们是不是真的实现了单例,其实这种模式在多线程模式下是不一定能实现单例模式的稍后我会在后面用一个例子来证明他,现在我们来考虑线程安全,你说synchronized ?,非常好 你已经抓住了问题的关键,我们接着往下走,修改后我们的代码变成了这样

  1. package unit;   
  2.   
  3. public class Singletnon   
  4. {   
  5.  private static Singletnon instance;   
  6.     
  7.  private Singletnon()   
  8.  {   
  9.      
  10.  }   
  11.  public synchronized static Singletnon getInstance()   
  12.  {   
  13.   if(instance==null)   
  14.   {   
  15.    instance=new Singletnon();   
  16.   }   
  17.   return instance;   
  18.  }   
  19. }   

 

这里这个方法已经线程安全了但是我们知道对一个方法进行线程安全资源消耗是非常大的我们更倾向于对代码的同步 修改后的代码
java 代码
  1. package unit;   
  2.   
  3. public class Singletnon   
  4. {   
  5.     private static Singletnon instance;   
  6.   
  7.     private static final Object key = new Object();   
  8.   
  9.     private Singletnon()   
  10.     {   
  11.   
  12.     }   
  13.   
  14.     public static Singletnon getInstance()   
  15.     {   
  16.         if (instance == null)   
  17.         {   
  18.             synchronized (key)   
  19.             {   
  20.                 if (instance == null)   
  21.                 {   
  22.                     instance = new Singletnon();   
  23.                 }   
  24.             }   
  25.         }   
  26.         return instance;   
  27.     }   
  28. }   

可以看到 zmo_xu在这里引入了一个Object 的对象key 为什么要引入一个key呢 用instance作为同步关键字不是更好吗!这个当然不行,因为线程同步锁锁定的是地址引用 如果你锁定的是instance的化 当你new的时候 地址引用就会改变.线程锁失效,额你说什么 不new 行不行..不new   不new.....不牛,我只能说这些想的人太牛了,要知道我们这里要保护的代码就是为了实例化,我们一定要牛(new)的拉!

单例模式就说到这里 下一次我们将介绍抽象工厂!

javastudy给出的代码补充在这里 !基本上思路是一样的 只是代码实现上有点区别

  1. //饿汉式:    
  2. public class EagerSingleton {    
  3. private EagerSingleton() { }   
  4.   
  5. public static EagerSingleton getInstance() {    
  6. return m_instance;    
  7. }   
  8.   
  9. /** @label Creates */    
  10. private static final EagerSingleton m_instance = new EagerSingleton();    
  11. }    

 

java 代码
    //懒汉式
  1. public class LazySingleton    
  2. {    
  3. private LazySingleton() { }   
  4.   
  5. synchronized public static LazySingleton getInstance()    
  6. {    
  7.    if (m_instance == null)    
  8.    {    
  9.        m_instance = new LazySingleton();    
  10.    }    
  11.    return m_instance;    
  12.   }   
  13. }
  14. //使用到才实例化 不使用不实例化,比如你使用此类的其他静态方法 而饿汉式只要用到就被实例化
java 代码
  1. //登记式:      
  2.      
  3. import java.util.HashMap;       
  4. public class RegSingletonChild extends RegSingleton       
  5. {       
  6. public RegSingletonChild() {}      
  7.      
  8. static public RegSingletonChild getInstance()       
  9. {       
  10. return (RegSingletonChild) RegSingleton.getInstance( "com.javapatterns.singleton.demos.RegSingletonChild" );       
  11. }      
  12.      
  13. public String about()       
  14. {       
  15. return "Hello, I am RegSingletonChild.";       
  16. }      
  17.      
  18. }      
  19. //这个 嘿嘿 不太明白 待会去看看他的父类原代码     

这个帖子的代码部分不会再修改了 就此代码部分封帖 ,希望大家 一起继续讨论共同提高

最后附上所有原代码

  • Singleton.rar (4.9 KB)
  • 描述: 单例模式的测试代码
  • 下载次数: 166
分享到:
评论
17 楼 yuankai 2008-04-23  
to seer_lee
final LazySingleton temp = new LazySingleton(); instance = temp;
java语言规范规定:final引用被赋值给另一个引用时必须被完整初始化,即instance = temp肯定会在初始化构造函数结束后发生.
这个好像不对吧?
final FinalTest fin = null;
FinalTest ft = null;
ft = fin;
我这样用也没有报错啊?
16 楼 seer_lee 2007-08-05  
另见:
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=4
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-toolbox.html
http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html?page=1
15 楼 seer_lee 2007-08-05  
double check懒初始化方法最早由Douglas Schmidt(ACE的作者)提出,double check的线程安全性依赖于处理器的内存同步模型和锁同步机制的内在实现。

java中,double check的线程安全性在某些情况下会被破坏。

非线程安全的几种情况:
1.优化编译器重排JVM字节码
此时可能导致instance = new Singletnon()的执行不像字面显示的那样,而是先创建一个对象,然后赋值给instance,最后执行<init>方法(即构造函数),
在这种情况下,一些线程可能看到赋完值的非null引用,而此时对象的构造函数还并没有被调用,即这些线程看到了脏的中间数据。
2.在多处理器共享存储器环境中,内存的更新不可见,
也可能导致一个线程看不到初始化线程的初始化最终结果,
而读取到中间状态的脏数据。

解决方法:
把instance设置为:private static volatile LazySingleton instance;
这样优化编译器就不会再对instance的赋值指令进行重排,
肯定是发生在<init>函数结束时,
在多处理器共享存储器环境下,也可以保证任何线程对instance的内存更新其他线程都可以看见

注意:
JSE5或者更高的版本中的Java Memory Model和Thread specification保证了上述解决方案的可行性,
在低版本中还会存在线程安全被破坏的情况。

详细可见参考资料:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
文章详细的讲述了Double Checking方法在Java中的表现。

下面是自己给出的另一种基于final关键字的方案,
没经过实践检验,自己只是觉得从逻辑上应该是对的,
请大家指正!
class LazySingleton {
  private static LazySingleton instance;
  private LazySingleton() {
  }
  public LazySingleton getInstance() {
    if (instance == null) {
      synchronized(this) {
if (instance == null) {
          final LazySingleton temp =
                          new  LazySingleton();
  instance = temp;
}
      }
     }
     return instance;
  }

}

final LazySingleton temp = new  LazySingleton(); instance = temp;
java语言规范规定:final引用被赋值给另一个引用时必须被完整初始化,即instance = temp肯定会在初始化构造函数结束后发生
14 楼 xiumu 2007-07-20  
多谢分享!
13 楼 暗之水晶 2007-07-18  
由于JAVA编译器会重排指令的关系,所以DOUBLE CHECK在JAVA中是无效的.
12 楼 zmo_xu 2007-06-13  
不会啊 我的附件里面代码里面有详细的测试的例子 你可以下载了试试 ..是有2个线程可以进去 但是到了 同步那里 因为第一个线程占掉了key对象  所以第二个线程只能等等,等他能访问key是 说明已经实例化好了 那么里面的instance==null必然是false;所以只能获得实例化好了的实例, 不信你可以看我的附件里面的代码
11 楼 zmo_xu 2007-06-12  
<br/>
<strong>javastudy 写道:</strong><br/>
<div class='quote_div'>
<div class='quote_div'>
<p> <span class='keyword'>public</span><span> </span><span class='keyword'>synchronized</span><span> </span><span class='keyword'>static</span><span> Singletnon getInstance()   </span> </p>
<li class='alt'><span>    {   </span> </li>
<li class=''><span>        </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)   </span> </li>
<li class='alt'><span>        {   </span> </li>
<li class=''><span>            </span><span class='keyword'>synchronized</span><span> (key)   </span> </li>
<li class='alt'><span>            {   </span> </li>
<li class=''><span>                </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)   </span> </li>
<li class='alt'><span>                {   </span> </li>
<li class=''><span>                    instance = </span><span class='keyword'>new</span><span> Singletnon();   </span> </li>
<li class='alt'><span>                }   </span> </li>
<li class=''><span>            }   </span> </li>
<li class='alt'><span>        }   </span> </li>
<li class='alt'>方法还是同步的啊 </li>
</div>
</div>
汉死了 好像是我手误 应该是这样
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'/>
<ol class='dp-j'>
    <li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>static</span><span> Singletnon getInstance()       </span></span></li>
    <li class=''><span>  </span></li>
    <li class='alt'><span>    {       </span></li>
    <li class=''><span>        </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)       </span></li>
    <li class='alt'><span>        {       </span></li>
    <li class=''><span>            </span><span class='keyword'>synchronized</span><span> (key)       </span></li>
    <li class='alt'><span>            {       </span></li>
    <li class=''><span>                </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)       </span></li>
    <li class='alt'><span>                {       </span></li>
    <li class=''><span>                    instance = </span><span class='keyword'>new</span><span> Singletnon();       </span></li>
    <li class='alt'><span>                }       </span></li>
    <li class=''><span>            }      </span></li>
    <li class='alt'><span>        }    </span></li>
    <li class=''><span>     retuen instance;   </span></li>
    <li class='alt'><span>}   </span></li>
</ol>
</div>
<br/>
<br/>
<br/>
<br/>
10 楼 Terry_Y 2007-06-12  
学习了
9 楼 demoon 2007-06-12  
hao dongxi
8 楼 zmo_xu 2007-06-11  
javastudy 你的懒汉式的和我上面的是一样的啊 我的只是里面加入了线程安全在方法的内部  而你是加载在整个方法上
另外顺便把你的代码引用到我的帖子里面去了
7 楼 laiseeme 2007-06-11  
谁给个三种实现方式的链接
6 楼 longrm 2007-06-11  
javastudy 写道
zmo_xu 写道
三种?你是说?能说一下什么意思吗是关系到安全性什么的还是实现上的.我这里并没有说我这里给出了所有的方式,我只是给出了几个经典的常用模式,而且我也还不知道怎么实现延期实例化的实现,望高手赐教


懒汉式,饿汉式,登记式

.......

到底是什么啦,说清楚点,最好像楼主那样把相应的代码贴出来,谢谢!
5 楼 alexander_xu 2007-06-11  
学习中,希望持续进行!并且能给点好的实例
4 楼 zmo_xu 2007-06-10  
三种?你是说?能说一下什么意思吗是关系到安全性什么的还是实现上的.我这里并没有说我这里给出了所有的方式,我只是给出了几个经典的常用模式,而且我也还不知道怎么实现延期实例化的实现,望高手赐教
3 楼 gitahwang 2007-06-10  
不错,学习中,希望楼主继续,能够学到更多的模式,
也欢迎高手,指出其中的不足,大家一起讨论会更好
2 楼 Eastsun 2007-06-10  
<br/>
<div class='quote_div'><font><blockquote dir='ltr' style='margin-right: 0px;'><font><font><font>
<p> </p>
<p><font/></p>
</font></font></font></blockquote></font>这里这个方法已经线程安全了但是我们知道对一个方法进行线程安全资源消耗是非常大的我们更倾向于对代码的同步 修改后的代码
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'/>
<ol class='dp-j'>
    <li class='alt'><span><span class='keyword'>package</span><span> unit;   </span></span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span/><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span> Singletnon   </span> </li>
    <li class=''><span>{   </span> </li>
    <li class='alt'><span>    </span><span class='keyword'>private</span><span> </span><span class='keyword'>static</span><span> Singletnon instance;   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>    </span><span class='keyword'>private</span><span> </span><span class='keyword'>static</span><span> </span><span class='keyword'>final</span><span> Object key = </span><span class='keyword'>new</span><span> Object();   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>    </span><span class='keyword'>private</span><span> Singletnon()   </span> </li>
    <li class=''><span>    {   </span> </li>
    <li class='alt'><span>  </span> </li>
    <li class=''><span>    }   </span> </li>
    <li class='alt'><span>  </span> </li>
    <li class=''><span>    </span><span class='keyword'>public</span><span> </span><span class='keyword'>synchronized</span><span> </span><span class='keyword'>static</span><span> Singletnon getInstance()   </span> </li>
    <li class='alt'><span>    {   </span> </li>
    <li class=''><span>        </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)   </span> </li>
    <li class='alt'><span>        {   </span> </li>
    <li class=''><span>            </span><span class='keyword'>synchronized</span><span> (key)   </span> </li>
    <li class='alt'><span>            {   </span> </li>
    <li class=''><span>                </span><span class='keyword'>if</span><span> (instance == </span><span class='keyword'>null</span><span>)   </span> </li>
    <li class='alt'><span>                {   </span> </li>
    <li class=''><span>                    instance = </span><span class='keyword'>new</span><span> Singletnon();   </span> </li>
    <li class='alt'><span>                }   </span> </li>
    <li class=''><span>            }   </span> </li>
    <li class='alt'><span>        }   </span> </li>
    <li class=''><span>        </span><span class='keyword'>return</span><span> instance;   </span> </li>
    <li class='alt'><span>    }   </span> </li>
    <li class=''><span>}   </span> </li>
</ol>
</div>
</div>
<p><br/>
<br/>
话说用了两个<span class='keyword'>synchronized同步性能就会提高么?</span></p>
<p><span class='keyword'>感觉里面那个<span class='keyword'>synchronized</span><span> (key) 有画蛇添足之嫌.</span></span></p>
<p><span class='keyword'><span/></span></p>
<p><span class='keyword'><span>关于<span> Singleton 的线程安全问题,Effective JAVA里面已经讲的很清楚,可以用initialize-on-demand holder class:</span></span></span></p>
<p><span class='keyword'><span><span/></span></span></p>
<span class='keyword'><span><span><font>
<div class='code_title'>java 代码</div>
<div class='dp-highlighter'>
<div class='bar'/>
<ol class='dp-j'>
    <li class='alt'><span><span class='keyword'>public</span><span> </span><span class='keyword'>class</span><span>  Singleton {   </span></span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>         </span><span class='keyword'>private</span><span> </span><span class='keyword'>static</span><span> </span><span class='keyword'>class</span><span>  SingletonHandler{   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>                      </span><span class='keyword'>static</span><span> </span><span class='keyword'>final</span><span>  Singleton  singleton =</span><span class='keyword'>new</span><span>  Singleton();    </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>         }   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>         </span><span class='keyword'>private</span><span>  Singleton (){}   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>         </span><span class='keyword'>public</span><span> </span><span class='keyword'>static</span><span>  Singleton getInstance(){   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>                    </span><span class='keyword'>return</span><span> SingletonHandler.singleton;   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>         }   </span> </li>
    <li class=''><span>  </span> </li>
    <li class='alt'><span>}   </span> </li>
</ol>
</div>
<p><br/>
</p>
</font></span></span></span>
<p><br/>
<br/>
</p>
1 楼 zmo_xu 2007-06-10  
莫名的提交失败,受不了,我只能修改点提交再修改,有点受不了的说

相关推荐

Global site tag (gtag.js) - Google Analytics