singleton单态模式的实现方式

参看了crazybob的关于单态模式的文章http://crazybob.org/2007/01/lazy-loading-singletons.html,非常受益。最近在学习设计模式,有必要整理下singletion的几种实现方式:
简单说来,大体是有三种实现方式:
1、饿汉式 在下面这个例子中,这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造子会被调用。这时候,单例类的惟一实例就被创建出来了。

//饿汉式:
public class EagerSingleton {
/** @label Creates */
private static final EagerSingleton m_instance = new EagerSingleton();
/**
* 私有的默认构造函数
*/
private EagerSingleton() { }

/**
* 静态工厂方法
*/
public static EagerSingleton getInstance() {
return m_instance;
}
}

2、懒汉式
Java代码

//懒汉式
//使用到才实例化 不使用不实例化,比如你使用此类的其他静态方法 而饿汉式只要用到就被实例化
public class LazySingleton
{
private LazySingleton() { }

//对静态工厂方法使用了同步化,以处理多线程环境
public synchronized static LazySingleton getInstance()
{
if (m_instance == null)
{
m_instance = new LazySingleton();
}
return m_instance;
}
}


注:(1)、从资源利用效率角度来讲,饿汉式比懒汉式单例类稍差些。
  (2)、从速度和反应时间角度来讲,饿汉式比懒汉式单例类稍好些。
3、登记式
Java代码

//登记式:
import java.util.HashMap;
public class RegSingleton
{
static private HashMap m_registry = new HashMap();
static
{
RegSingleton x = new RegSingleton();
m_registry.put( x.getClass().getName() , x);
}
/**
* 保护的默认构造子
*/
protected RegSingleton() {}
/**
* 静态工厂方法,返还此类惟一的实例
*/
static public RegSingleton getInstance(String name)
{
if (name == null)
{
name = "com.javapatterns.singleton.demos.RegSingleton";
}
if (m_registry.get(name) == null)
{
try
{
m_registry.put( name,Class.forName(name).newInstance() ) ;
}
catch(Exception e)
{
System.out.println("Error happened.");
}
}
return (RegSingleton) (m_registry.get(name) );
}
/**
* 一个示意性的商业方法
*/
public String about()
{
return "Hello, I am RegSingleton.";
}
}


它的子类RegSingletonChild 需要父类的帮助才能实例化。
Java代码

import java.util.HashMap;
public class RegSingletonChild extends RegSingleton
{
public RegSingletonChild() {}
/**
* 静态工厂方法
*/
static public RegSingletonChild getInstance()
{
return (RegSingletonChild)
RegSingleton.getInstance( "com.javapatterns.singleton.demos.RegSingletonChild" );
}
/**
* 一个示意性的商业方法
*/
public String about()
{
return "Hello, I am RegSingletonChild.";
}
}

缺点:
1、RegSingletonChild类的构造函数是public,就等于允许了以这样方式产生实例而不在父类的登记中。

2、GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。


下面我们接着补充:
我们使用的多为前两种方式:饿汉式和懒汉式。究竟选择哪一个,我们这里有一个原则。在系统上线使用时,你一般都需要一次性加载所有的单态,以便于尽早发现可能出现的问题;而在开发和测试阶段,你只是需要加载那些必须的单态而不会浪费时间。

针对懒汉式(用到时加载),我们进一步拓展开来:上面给的代码实现方式,是使用原始的老的线程同步机制,以保证线程安全。
在java 1.5 之后,出现了volatile 关键字,此关键字能保证其修饰的变量不允许有不同于“主”内存区域(Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里的i1值是3)的变量拷贝,换句话说,一个变量经volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。
接着就有了基于Double-Checked Locking(DCL)的懒汉式,这种方式通过“主”内存区对volatile修饰的变量域进行检查,并且在必要的时候进行加锁。

public class Singleton {
private volatile Singleton instance;

private Singleton(){}

public Singleton getInstance(){
if(null == instance){
synchronized (Singleton.class) {
if(null == instance)
instance = new Singleton();
}
}
return instance;
}
}

但DCL方式的懒汉,使用并不广泛,因为相比较传统的方法加锁机制,DCL方式并不快多少。所以大多数人仍在使用传统的方式。
但现在有一种懒汉方式,非常的流行,采取的是Initialization on Demand Holder(IODH),此方式代码量不多,并且线程同步问题解决的非常完美。代码如下:

public class Singleton {
private static class SingletonHolder{
private static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}

单例模式,先学到这里。还有巨多的模式等着我呢,慢慢来。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值