您的位置:首页 > 其它

Double-checked locking and the Singleton pattern

2014-02-06 22:54 423 查看
Java单例模型非常常用,要实现单例,需要将相应的构造函数声明为private,然后通过静态方法getInstance()方法返回一个实例对象,这里有两种方法:

1、静态的对象直接初始化,调用静态方法getInstance()时直接返回

 
import java.util.*;
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() { }

public static Singleton getInstance() {
return instance;
}
}


 

 2、与1不同,对象的初始化是在getInstance()方法中完成的,其中getInstance()比较复杂,所用技术就是Double-checked locking

 
import java.util.*;
class Singleton
{
private static Singleton instance;

private Singleton() {}

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


 http://www.ibm.com/developerworks/java/library/j-dcl.html这篇文章详细讲解了Double-checked locking的来源以及出现的问题。

 

从程序代码角度来看,getInstance()可以完美的解决单例问题,既解决了首次方法调用的同步问题,同时也能保证以后的方法调用的未加入同步已解决性能问题。但文章指出事实并非如此,方法并不能完全保证方法的正确执行。其中涉及到构造函数instance = new Singleton()的底层实现:

 
mem = allocate();              //Allocate memory for Singleton object.
instance = mem;               //Note that instance is now non-null, but
//has not been initialized.
ctorSingleton(instance);   //Invoke constructor for Singleton passing
//instance.


 instance可以在构造函数未执行前获得地址索引成non-null,当多个线程操作时变出现问题。这是编译器的乱序写问题,文章写于02年5月,其中提到的JIT compiler是Sun JDK 1.2.1,不是所有编译器都能够保证解决乱序写问题。

文章给出一个测试乱续写问题,验证String的不变性。不过本人用的是jdk6,跑了半天没跑出什么结果。

class StringCreator extends Thread {
MutableString ms;

public StringCreator(MutableString muts) {
ms = muts;
}

public void run() {
while (true)
ms.str = new String("hello"); // 1
}
}

class StringReader extends Thread {
MutableString ms;

public StringReader(MutableString muts) {
ms = muts;
}

public void run() {
while (true) {
if (!("hello".equals(ms.str))) // 2
{
System.out.println("String is not immutable!");
break;
}
}
}
}

class MutableString {
public String str = "hello"; // 3

public static void main(String args[]) {
MutableString ms = new MutableString(); // 4
new StringCreator(ms).start(); // 5
new StringReader(ms).start(); // 6
}
}


 

 文章最后指出单例模式最好选择两种方式:1、就是本文开始提到的第一种方法,2、是在getInstance()方法中初始化单例对象,但该方法需要声明为同步方法,即加个synchronized,优劣可想而知
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: