Singleton Pattern 单态模式

Singleton Pattern 单态模式。

这个模式较简单,就是为了保证一个类只有一个实例,用一个入口来获取该实例。

例子如下(代码来自维基):

class Foo {
private static Helper helper = null;
private Foo(){}
public static Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}

// other functions and members...
}

由于构造方法设为了私有,所以其它类无法使用new 来生成该类实例,只有通过
该类自己提供的方法来获取实例。这个获取实例的方法通总是使用第一次创建的
实例而保证其它类总是使用同一个实例。

本来很简单,但是在多线程情况下则会有些麻烦。两个线程都有可能执行到了第
6行,这样就有可能创建2个实例。过程如下(线程1简称为T1,线程2 简称T2):

T1 5 (5代表第5行)
T2 5
T1 6
T1 7 (线程1返回一个实例)
T2 6
T2 7 (线程2返回另一个实例)

这样就破坏了实例唯一性。如果这两个实例状态不会变化,倒也无所谓,就是多
花点内存养两个对象。但如果这两个实例涉及到计数器等状态变化,就埋下了隐患。

解决办法是加上同步关键字,如下:

    public static synchronized Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}


问题是解决了,但效率却降低了。因为每次获取实例都会执行同步运算。实际上
没必要。一旦实例创建并返回,以后再获取实例就不会执行第6行。为了发生几率
很小的事件而每次同步的方法,代价太大了。

不同虚拟机对同步方法的执行效率不同,早期虚拟机执行同步效率非常的低。现
在改进了很多,但进入线程体、离开线程体、加锁、解锁这些步骤仍然耗费性能。

所以,后来发明了双检查锁模式。(Double-Checked Locking Pattern)。在
《DesignPatternsExplained》一书中,作者专门分析了这种方法的优势,并给出
了代码。代码如下:

class USTax {
private static USTax instance;
private synchronized USTax(){};
public static USTax getlnstance () {
if(instance== null)
instance= new USTax();
return instance;
}
}


不想,书上印错了,这让很多人包括我迷惑良久。后来书本经过更正,变为如下形式,

class USTax {
private static USTax instance;
private USTax(){};
public static synchronized void doSyn(){
if(instance== null){
instance= new USTax();
instance.setSomething();}//这句是我加的,为方便接下来的解释。
}
public static USTax getlnstance () {
if(instance== null)
doSyn();
return instance;
}
}


到此,算是万事大吉了。不料这种双重检查模式仍然有问题,并不能解决多线程
的问题。由于这本书的广泛流传,这种模式也广为人知。当这种模式的错误之处
被发现后,很快就出现了对这种模式广泛的讨伐之声。可见,影响越大,一旦有
错,被讨伐的力度也就越大。

我们以上面这段代码为例,看看问题是怎样产生的,

T1 10 (线程1执行到第10行)
T1 5
T1 6
T2 10
T2 12

也就是说,线程1还没有完全构造完这个对象(还没有执行第7行),线程2就返回
这个半成品对象了。所以,这种谬种流传的 Double-Checked Locking Pattern也
就寿终正寝,不能再被使用了。

那这种多线程出现的问题该怎么做呢,解决方法也很简单,如下即可,

class USTax {
private static USTax instance = new USTax();
private USTax(){};
public static synchronized void doSyn(){
if(instance== null)
instance= new USTax();
}
public static USTax getlnstance () {
return instance;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值