Double-checked locking and the Singleton pattern

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,优劣可想而知

你可能感兴趣的:(jdk,thread,IBM,J#,sun)