昨天看Struts案例开发,看到以下一段代码:

/*导入包操作省略*/

public class ControlDB{

private static ControlDB controlDB=null;

//构造方法私有化,为单态做准备

private ControlDB(){}

public static ControlDB getInstance(){

if(controlDB==null) {

controlDB=new ControlDB();

}

        return controlDB;

}

……

}

一般来说,java是以多态性著称的,为何此处要放弃多态性这一“光荣传统”?我上网查得以下结论:

 

单态(Singleton)定义:

  单态模式主要作用是保证在Java应用程序中,一个类Class只有一个实例或者有限个实例的存在。

    单态模式通过以下途径实现:构造方法私有化,同时添加功能为产生实例类的方法getInstance()。构造方法私有化,使得别的类不能条用该类的构造方法,也就不能通过构造方法产生该类的实例。而该类的实例只能通过类方法来产生。这样做可以避免构造方法被错误的调用。因为如果构造方法是公有的,就会由于我用的编译器和别人用的编译器不同,从而导致调用构造函数行为未可知。解决这种情况就会用构造方法私有化,然后通过调用一个生成函数生成该类的实例。com组件里这样用的很多。

  单态模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。在windows中也有此设计的存在。Windows中的回收站,所有的盘都共享同一个回收站,这就是一个典型的单态设计。

 

 

 

使用Singleton注意事项

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

  单态模式的演化

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

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

  一,首先最简单的单态模式,单态模式1

  import java.util.*;

  class Singleton

  {

  private static Singleton instance;

  private Vector v;

  private boolean inUse;

  private Singleton()

  {

  v = new Vector();

  v.addElement(new Object());

  inUse = true;

  }

  public static Singleton getInstance()

  {

  if (instance == null)     //1

  instance = new Singleton();  //2

  return instance;       //3

  }

  }

  这个单态模式是不安全的,为什么说呢 ?因为没考虑多线程,如下情况

  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

  public static synchronized Singleton getInstance()

  {

  if (instance == null)     //1

  instance = new Singleton();  //2

  return instance;       //3

  }

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

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

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) {

  instance = new Singleton();

  }

  }

  return instance;

  }

  同步改成块同步,而不使用函数同步,但是仔细分析,

  又回到了模式一的状态,再多线程的时候根本没有解决问题

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

  public static Singleton getInstance()

  {

  if (instance == null)

  {

  synchronized(Singleton.class) {  //1

  if (instance == null)     //2

  instance = new Singleton();  //3

  }

  }

  return instance;

  }

  这样,模式一中提到的问题解决了。不会出现多次实例化的现象

  当第一次进入的时候,保正实例化时候的单态,在实例化后,多线程访问的时候直接返回,不须要进入同步模块,既实现了单态,又没有损失性能。表面上看我们的问题解决了,但是再仔细分析

  我们来假象这中情况

  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

-----------------------------------------