现在的位置: 首页 > 综合 > 正文

单件模式

2014年01月16日 ⁄ 综合 ⁄ 共 1640字 ⁄ 字号 评论关闭

   在某些情况下,比如设备驱动程序的对象,我们自始至终只需要1个对象,因为如果制造出多个实例就会导致许多问题产生,这就需要单件模式。

 

 

public class ChocolateBoiler {

private boolean empty;

private boolean boiled;

private static ChocolateBoiler uniqueInstance;

 

private ChocolateBoiler() {

empty = true;

boiled = false;

}

 

public static ChocolateBoiler getInstance() {

if (uniqueInstance == null) {

System.out.println("Creating unique instance of Chocolate Boiler");

uniqueInstance = new ChocolateBoiler();

}

System.out.println("Returning instance of Chocolate Boiler");

return uniqueInstance;

}

 

public void fill() {

if (isEmpty()) {

empty = false;

boiled = false;

// fill the boiler with a milk/chocolate mixture

}

}

 

public void drain() {

if (!isEmpty() && isBoiled()) {

// drain the boiled milk and chocolate

empty = true;

}

}

 

public void boil() {

if (!isEmpty() && !isBoiled()) {

// bring the contents to a boil

boiled = true;

}

}

 

public boolean isEmpty() {

return empty;

}

 

public boolean isBoiled() {

return boiled;

}

}

但是如上的单件模式在多线程模式下容易出现不为人知的BUG,如图所示:
如何解决上述问题呢?
1、同步的方法:将getInstance()变成synchronized()方法,public static synchronized ChocolateBoiler getInstance();但是只有第一次执行该方法时,才真正需要同步,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。而且同步的方法会使程序效率降低100倍,因此如果getInstance()程序用在频繁运行的地方,则需要重新考虑。
2、使用"急切"创建实例,而不是延迟实例化的做法
如果程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切创建此单件,如下所示,当加载这个类时马上创建此唯一的单件实例,JVM保证任何线程访问uniqueInstance静态变量之前,一定先创建此实例。

3、用"双重检查加锁",在getInstance()中减少使用同步

 

 

但要注意:双重加锁方式不适应于1.4及更早版本的Java,很多JVM对volatile的实现会导致双重加锁失败。

 

 

一些问题:

 

1、在Java中,全局变量基本上是对对象的静态引用,单件模式确保类只有1个实例并提供全局访问,全局变量可以提供全局访问,但是不能确保只有1个实例。

 

2、单件继承:因为构造器是私有的,不能用私有构造器来扩展类,因此需要将其改为public或protected(这样就不算真正的单件了)。最好不要继承单件,因为会造成子类共享static的那个量。

 

3、类加载器:如果在程序中有多个类加载器,而且同时使用了单件模式,会导致同一个类被加载多次,会出现多个单件并存的怪异现象。解决办法是:自行指定类加载器,并指定同一个类加载器。

 

4、如果使用JVM1.2或之前版本,你必须建立单件注册表,以免垃圾收集器把单件回收。

 

抱歉!评论已关闭.