`
rq2_79
  • 浏览: 240481 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java中的模式 --单态

阅读更多

单态定义:
        Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。

        Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。

使用Singleton注意事项:
        有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的

单态模式的演化:
        单态模式是个简单的模式,但是这个简单的模式也有很多复杂的东西。

(注意:在这里补充一下,现在单态模式其实有一个写法是不错的见这里:http://www.blogjava.net/dreamstone/archive/2007/02/27/101000.html,但还是建议看完这篇文章,因为解释的事情是不一样的,这里说的是为什么double-checked不能使用.)
一,首先最简单的单态模式,单态模式1

java 代码
 
  1. import java.util.*;  
  2. class Singleton  
  3. {  
  4.   private static Singleton instance;  
  5.   private Vector v;  
  6.   private boolean inUse;  
  7.   
  8.   private Singleton()  
  9.   {  
  10.     v = new Vector();  
  11.     v.addElement(new Object());  
  12.     inUse = true;  
  13.   }  
  14.   
  15.   public static Singleton getInstance()  
  16.   {  
  17.     if (instance == null)          //1  
  18.       instance = new Singleton();  //2  
  19.     return instance;               //3  
  20.   }  
  21. }  


java 代码

  1. import java.util.*;  
  2. class Singleton  
  3. {  
  4.   private static Singleton instance;  
  5.   private Vector v;  
  6.   private boolean inUse;  
  7.   
  8.   private Singleton()  
  9.   {  
  10.     v = new Vector();  
  11.     v.addElement(new Object());  
  12.     inUse = true;  
  13.   }  
  14.   
  15.   public static Singleton getInstance()  
  16.   {  
  17.     if (instance == null)          //1  
  18.       instance = new Singleton();  //2  
  19.     return instance;               //3  
  20.   }  
  21. }  

        这个单态模式是不安全的,为什么说呢 ?因为没考虑多线程,如下情况
        Thread 1 调用getInstance() 方法,并且判断instance是null,然後进入if模块,在实例化instance之前,Thread 2抢占了Thread 1的cpu。Thread 2 调用getInstance() 方法,并且判断instance是null,然後进入if模块,Thread 2 实例化instance 完成,返回Thread 1 再次实例化instance
        这个单态已经不在是单态

 

二,为了解决刚才的问题:单态模式2

java 代码
 
  1. public static synchronized Singleton getInstance()  
  2. {  
  3.   if (instance == null)          //1  
  4.     instance = new Singleton();  //2  
  5.   return instance;               //3  
  6. }  

        采用同步来解决,这种方式解决了问题,但是仔细分析正常的情况下只有第一次时候,进入对象的实例化,须要同步,其它时候都是直接返回已经实例化好的instance不须要同步,大家都知到在一个多线程的程序中,如果同步的消耗是很大的,很容易造成瓶颈

 

三,为了解决上边的问题:单态模式3

java 代码
 
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)  
  4.   {  
  5.     synchronized(Singleton.class) {  
  6.       instance = new Singleton();  
  7.     }  
  8.   }  
  9.   return instance;  
  10. }  

        同步改成块同步,而不使用函数同步,但是仔细分析,又回到了模式一的状态,再多线程的时候根本没有解决问题

 

四,为了对应上边的问题:单态模式4,也就是很多人采用的Double-checked locking

java 代码
 
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)  
  4.   {  
  5.     synchronized(Singleton.class) {  //1  
  6.       if (instance == null)          //2  
  7.         instance = new Singleton();  //3  
  8.     }  
  9.   }  
  10.   return instance;  
  11. }  

    这样,模式一中提到的问题解决了。不会出现多次实例化的现象。当第一次进入的时候,保正实例化时候的单态,在实例化后,多线程访问的时候直接返回,不须要进入同步模块,既实现了单态,又没有损失性能。表面上看我们的问题解决了,但是再仔细分析:
我们来假象这中情况:
        Thread 1 :进入到//3位置,执行new Singleton(),但是在构造函数刚刚开始的时候被Thread2抢占cpu
        Thread 2 :进入getInstance(),判断instance不等于null,返回instance,
        (instance已经被new,已经分配了内存空间,但是没有初始化数据)
        Thread 2 :利用返回的instance做某些操做,失败或者异常
        Thread 1 :取得cpu初始化完成
        过程中可能有多个线程取到了没有完成的实例,并用这个实例作出某些操做。
-----------------------------------------
出现以上的问题是因为
mem = allocate();             //分配内存
instance = mem;               //标记instance非空
                              //未执行构造函数,thread 2从这里进入
ctorSingleton(instance);      //执行构造函数
                              //返回instance
------------------------------------------                             

 

五,证明上边的假想是可能发生的,字节码是用来分析问题的最好的工具,可以利用它来分析
下边一段程序:(为了分析方便,所以渐少了内容)
字节码的使用方法见这里,利用字节码分析问题
class Singleton
{
  private static Singleton instance;
  private boolean inUse;
  private int val; 

  private Singleton()
  {
    inUse = true;
    val = 5;
  }
  public static Singleton getInstance()
  {
    if (instance == null)
      instance = new Singleton();
    return instance;
  }
}
得到的字节码                           
;asm code generated for getInstance
054D20B0   mov         eax,[049388C8]      ;load instance ref
054D20B5   test        eax,eax             ;test for null
054D20B7   jne         054D20D7
054D20B9   mov         eax,14C0988h
054D20BE   call        503EF8F0            ;allocate memory
054D20C3   mov         [049388C8],eax      ;store pointer in
                                           ;instance ref. instance 
                                           ;non-null and ctor
                                           ;has not run
054D20C8   mov         ecx,dword ptr [eax]
054D20CA   mov         dword ptr [ecx],1   ;inline ctor - inUse=true;
054D20D0   mov         dword ptr [ecx+4],5 ;inline ctor - val=5;
054D20D7   mov         ebx,dword ptr ds:[49388C8h]
054D20DD   jmp         054D20B0

上边的字节码证明,猜想是有可能实现的

六:好了,上边证明Double-checked locking可能出现取出错误数据的情况,那么我们还是可以解决的

java 代码
 
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)  
  4.   {  
  5.     synchronized(Singleton.class) {      //1  
  6.       Singleton inst = instance;         //2  
  7.       if (inst == null)  
  8.       {  
  9.         synchronized(Singleton.class) {  //3  
  10.           inst = new Singleton();        //4  
  11.         }  
  12.         instance = inst;                 //5  
  13.       }  
  14.     }  
  15.   }  
  16.   return instance;  
  17. }  

利用Double-checked locking 两次同步,中间变量,解决上边的问题。
(下边这段话我只能简单的理解,翻译过来不好,所以保留原文,list 7是上边的代码,list 8是下边的
The code in Listing 7 doesn't work because of the current definition of the memory model.
 The Java Language Specification (JLS) demands that code within a synchronized block
 not be moved out of a synchronized block. However, it does not say that
 code not in a synchronized block cannot be moved into a synchronized block.
A JIT compiler would see an optimization opportunity here.
This optimization would remove the code at
//4 and the code at //5, combine it and generate the code shown in Listing 8:)
------------------------------------------------- 
list 8
java 代码
 
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)  
  4.   {  
  5.     synchronized(Singleton.class) {      //1  
  6.       Singleton inst = instance;         //2  
  7.       if (inst == null)  
  8.       {  
  9.         synchronized(Singleton.class) {  //3  
  10.           //inst = new Singleton();      //4  
  11.           instance = new Singleton();                
  12.         }  
  13.         //instance = inst;               //5  
  14.       }  
  15.     }  
  16.   }  
  17.   return instance;  
  18. }  

If this optimization takes place, you have the same out-of-order write problem we discussed earlier.
如果这个优化发生,将再次发生上边提到的问题,取得没有实例化完成的数据。
-------------------------------------------------

 

以下部分为了避免我翻译错误误导打家,保留原文

Another idea is to use the keyword volatile for the variables inst and instance.
According to the JLS (see Resources), variables declared volatile are supposed to
be sequentially consistent, and therefore, not reordered.
But two problems occur with trying to use volatile to fix the problem with
double-checked locking:

The problem here is not with sequential consistency.
Code is being moved, not reordered.

Many JVMs do not implement volatile correctly regarding sequential consistency anyway.
The second point is worth expanding upon. Consider the code in Listing 9:

Listing 9. Sequential consistency with volatile

java 代码
 
  1. class test  
  2. {  
  3.   private volatile boolean stop = false;  
  4.   private volatile int num = 0;  
  5.   
  6.   public void foo()  
  7.   {  
  8.     num = 100;    //This can happen second  
  9.     stop = true;  //This can happen first  
  10.     //...  
  11.   }  
  12.   
  13.   public void bar()  
  14.   {  
  15.     if (stop)  
  16.       num += num;  //num can == 0!  
  17.   }  
  18.   //...  
  19. }  
According to the JLS, because stop and num are declared volatile,

they should be sequentially consistent. This means that if stop is ever true,
num must have been set to 100.
However, because many JVMs do not implement the sequential consistency feature of volatile,
you cannot count on this behavior.
Therefore, if thread 1 called foo and thread 2 called bar concurrently,
thread 1 might set stop to true before num is set to 100.
This could lead thread 2 to see stop as true, but num still set to 0.
There are additional problems with volatile and the atomicity of 64-bit variables,
but this is beyond the scope of this article.
See Resources for more information on this topic.

简单的理解上边这段话,使用volatile有可能能解决问题,volatile被定义用来保正一致性,但是很多虚拟机
并没有很好的实现volatile,所以使用它也会存在问题。

最终的解决方案:
 (1),单态模式2,使用同步方法
 (2),放弃同步,使用一个静态变量,如下

java 代码
 
  1. class Singleton  
  2. {  
  3.   private Vector v;  
  4.   private boolean inUse;  
  5.   private static Singleton instance = new Singleton();  
  6.   
  7.   private Singleton()  
  8.   {  
  9.     v = new Vector();  
  10.     inUse = true;  
  11.     //...  
  12.   }  
  13.   
  14.   public static Singleton getInstance()  
  15.   {  
  16.     return instance;  
  17.   }  
  18. }  

但使用静态变量也会存在问题,问题见 这篇文章

 

而且如在文章开头提到的,使用EJB跨服务器,跨JVM的情况下,单态更是问题

好了是不是感觉单态模式根本没法使用了,其实上边都是特殊情况,这中特殊情况的出现是有条件的,只要
根据你的具体应用,回避一些,就能解决问题,所以单态还是可以使用的。但是在使用前慎重,自己考虑好自己
的情况适合哪种情况。

参考
http://www.jdon.com/designpatterns/singleton.htm

Double-checked locking and the Singleton pattern

When is a singleton not a singleton?

分享到:
评论

相关推荐

    java设计模式-图解-附代码

    ### Java设计模式详解 #### 创建型模式 **1.1 工厂方法(Factory Method)** 工厂方法模式定义了一个创建对象的接口,但允许子类决定实例化哪一个类。这使得一个类的实例化可以被推迟到其子类。这种模式在以下...

    单态模式1---马克-to-win Java视频

    单态模式 马克-to-win Java视频 Singleton 单例模式

    单态模式2---马克-to-win java视频

    单态模式 马克-to-win java视频 Singleton 单例模式

    单态模式3---马克-to-win java视频

    单态模式 马克-to-win java视频 Singleton 单例模式

    Java中单态设计模式

    ### Java中单态设计模式详解 #### 一、引言 设计模式是在长期软件开发过程中提炼出来的最佳实践,它们提供了一种标准化的方式用于解决特定类型的问题。对于Java开发者而言,熟悉并掌握各种设计模式是非常重要的,...

    【Java设计模式】单态模式

    单态模式是在面向对象设计中实现类似单例行为的一种替代方法,确保Java应用程序中的共享状态。它强制实现一种独特的行为,即类的所有实例共享相同的状态。与单例模式不同,单例模式将类限制为单个实例,单态模式允许...

    JAVA设计模式(单态模式和简单工厂模式)

    Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 在项目的很多地方都会用到它,比如说数据库的链接。 使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于...

    Java设计模式-图解-附代码

    1.1.4 单态模式 13 1.1.5 原型模式 15 1.2 结构型模式 17 1.2.1 适配器模式 17 1.2.2 桥接模式 19 1.2.3 组合模式 23 1.2.4 装饰模式 26 1.2.5 外观模式 29 1.2.6 享元模式 32 1.2.7 代理模式 34 1.3 行为型模式 37 ...

    Java实现的单态登陆

    在IT行业中,单态登录(Singleton Login)是一种常见的设计模式,尤其在Web应用程序开发中,用于确保用户在系统内只需登录一次,即可访问多个相互关联的子系统或应用,无需重复登录。本篇将深入探讨Java如何实现单态...

    DataSource的单态模式(SingleTon)程序---马克-to-win java视频

    DataSource的单态模式(SingleTon)程序---马克-to-win java视频 数据源 单态模式 dataSource Singleton

    单态模式(代码)

    在Java、C#等面向对象编程语言中,单态模式的实现通常分为几种方式: 1. 饿汉式(Eager Initialization):在类加载时就完成实例化,这种方式保证了单例的唯一性,同时也确保了线程安全。但是可能会造成内存浪费,...

    23种设计模式的java实现

    单态模式 http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html 结构型 适配器模式 http://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html 桥接模式 ...

    单态模式(PPT)

    在Java中,单态模式的实现通常有两种常见方式:饿汉式和懒汉式。饿汉式是在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快,线程安全。而懒汉式是在类被加载后,第一次调用getInstance方法时才初始...

    JAVA 笔记-------------------

    5. **单态模式**:单态模式是一种设计模式,确保一个类只有一个实例,并提供全局访问点。在Java中,通常通过私有构造函数和静态工厂方法来实现单例。单例模式常用于控制资源的分配,如线程池、缓存或者数据库连接等...

    简单的Java单态例子

    这个一个简单的java例子,可以为新手加深对java单态模式的理解.....

    java代码笔记2010-05-31:继承this()的使用多态;单态;静态初始化;==比较的是地址;参数传递的是个值的拷贝还是引用的传递;循环菱形

    在Java编程语言中,本文将深入探讨几个关键概念,包括继承中的`this()`构造器调用、多态性、单态设计模式、静态初始化、对象引用的`==`比较以及参数传递机制。这些知识点是Java程序员必备的基础知识,对理解和编写...

    [浪曦原创]JAVA设计模式 第2讲 单态模式和简单工厂模式(jzkangta).rar

    Java设计模式是软件开发中的重要概念,它们是解决常见...这个压缩包资料“[浪曦原创]JAVA设计模式 第2讲 单态模式和简单工厂模式(jzkangta)”应包含了详细的讲解和示例代码,可以帮助深入理解和应用这两种设计模式。

    单态模式和简单工厂模式

    单态模式(Singleton Pattern)和简单工厂模式(Simple Factory Pattern)是软件设计中常见的两种创建型设计模式。在Java编程中,它们都是为了简化对象的创建过程,但各自有着不同的应用场景和特点。 单态模式是一...

    单态模式的设计和应用

    在Java中,实现单态模式主要有两种方式: 1. **饿汉式(Eager Initialization)**: ```java public class Singleton { private Singleton() {} // 构造函数私有化 private static Singleton instance = new ...

    单态模式和工厂模式.ppt

    单态模式和工厂模式是两种常见的设计模式,在软件开发中有着广泛的应用。它们各自解决了不同的问题,但在确保对象创建和管理方面提供了有力的支持。 **单态模式(Singleton Pattern)** 单态模式是一种创建型设计...

Global site tag (gtag.js) - Google Analytics