Double-checked locking and the Singleton pattern

来源:互联网 发布:原生安卓手机推荐 知乎 编辑:程序博客网 时间:2024/06/07 00:24

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"; // 3public static void main(String args[]) {MutableString ms = new MutableString(); // 4new StringCreator(ms).start(); // 5new StringReader(ms).start(); // 6}}

 

 文章最后指出单例模式最好选择两种方式:1、就是本文开始提到的第一种方法,2、是在getInstance()方法中初始化单例对象,但该方法需要声明为同步方法,即加个synchronized,优劣可想而知

0 0
原创粉丝点击