`

Java中的模式 --单态 (部分翻译 double-checked locking break)

阅读更多

 

Java中的模式 --单态 (部分翻译 double-checked locking break)

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

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

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

 

一:懒汉式单例模式

参考:http://www.ibm.com/developerworks/cn/java/j-dcl.html


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

public static synchronized Singleton getInstance()
{
  if (instance == null)          //1
    instance = new Singleton();  //2
  return instance;               //3
}


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

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

Java代码 复制代码 收藏代码
public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {
      instance = new Singleton();
    }
  }
  return instance;
}


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

4,为了对应上边的问题:单态模式4,也就是很多人采用的Double-checked locking(为了顾及SingletonLazy Initialization与效能问题,因而有了Double-check 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
------------------------------------------

 

二:饿汉式单例模式

 

 

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

 

 

三:结合模式

 

public   class  Singleton  {  
    static   class  SingletonHolder  {   
         static  Singleton instance  =   new  Singleton();   
    }  
     public   static  Singleton getInstance()  {   
          return  SingletonHolder.instance;   
     }  
}   
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics