论坛首页 Java企业应用论坛

以前帖子中double-checked locking问题的解决方法?

浏览 2460 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (8) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-07-23  

来自http://www.iteye.com/topic/157952

ajoo的方法
详细解释一下:
1。java 5以前不支持dcl。
2。java 5以后,根据spec,用volatile来声明那个static变量就可以。
3。实际上,一些vm不见得正确实现了volatile语义(比如x86上volatile就没有实现memory barrier),所以即使volatile也不见得好使。
4。最好的办法,不是用volatile,而是保证LazySingleton是immutable的,那么只要是java 5就保证必然成功。可以参考String的代码(虽然没用dcl,但是利用了final的语义来保证安全)。具体spec请参见:http://www.google.com/url?q=http://www.cs.umd.edu/users/jmanson/java/journal.pdf&sa=X&oi=unauthorizedredirect&ct=targetlink&ust=1201014498800543&usg=AFQjCNH1rrzRUKbvIfNL_3JDQ9unbZQydg
5。如果万一LazySingleton不是immutable。可以用一个final place holder来搞:

最后,这个是java 5新加的,以前是没有的。

 

class LazySingleton {   
  private LazySingleton() {}   
  private static class FinalPlaceHolder {   
    final LazySingleton singleton;   
    FinalPlaceHolder(LazySingleton s) {   
      this.singleton = s;   
    }   
  }   
  private static FinalPlaceHolder holder = null;   
  public static LazySingleton instance() {   
    if (holder == null) {   
      synchronized(LazySingleton.class) {   
        if (holder == null) {   
          holder = new FinalPlaceHolder(new LazySingleton());   
        }   
      }   
    }   
    return holder.singleton;   
  }   
}

 

我觉得上面的方法有问题,但又不太确定,所以拿出来和大家讨论一下。

对于holder = new FinalPlaceHolder(new LazySingleton());语句,

有可能得到一个非空的未执行初始化的对象引用,

那么return holder.singleton;就应该会出问题。

 

sorphi的方法
关于延迟初始化,Effective Java中有个例子


引用


// The initialize-on-demand holder class idiom
private static class FooHolder {
static final Foo foo = new Foo();
}
public static Foo getFoo() { return FooHolder.foo; }


The idiom takes advantage of the guarantee that a class will not be initialized until it is used
[JLS, 12.4.1]. When the getFoo method is invoked for the first time, it reads the field
FooHolder.foo, causing the FooHolder class to get initialized. The beauty of this idiom is
that the getFoo method is not synchronized and performs only a field access, so lazy
initialization adds practically nothing to the cost of access. The only shortcoming of the idiom
is that it does not work for instance fields, only for static fields.

上面的方法虽然是推崇的方法,但是也存在问题。

当new Foo(); 的操作发生异常时,问题就出现了:

线程1执行new Foo(); 抛出java.lang.ExceptionInInitializerError错误,

线程2再次执行时,则会发生java.lang.NoClassDefFoundError错误,

即上面的初始化一旦出错,那么将永远不会再被执行了。

这是不是很糟糕呢,假如DB停止1分钟,恰好这时候进行了第一次初始化出错了,

那么1分钟后,再次想初始化的时候,发现已经无法初始化了,还抛出了java.lang.NoClassDefFoundError错误。。。

而DCL就不会有这样的问题,但DCL却有其自身看似无法解决的问题。(即使用volatile,也并不是所以机器都行,而且volatile本身也是一半的同步代价。。)

似乎转了一个大圈子我们可以回到起点了,即干脆做方法级的同步算了,慢就慢点吧。

 

参考资料:

http://www.ibm.com/developerworks/cn/java/j-jtp03304/

   发表时间:2008-07-24  

一下是验证类初始化出错后,不能再次被初始化的代码。

 

public class Main {
	public static void main(String[] args) throws Exception {
		Main.class.getClassLoader().loadClass("ClassInit");
		Thread t = new Thread(new Runnable() {
			public void run() {
				try {
					ClassInit.test();
				} catch (Exception e) {
					System.out.println(e.toString());
				}
			}
		});
		t.start();
		t = new Thread(new Runnable() {
			public void run() {
				try {
					ClassInit.test();
				} catch (Exception e) {
					System.out.println(e.toString());
				}

			}
		});
		t.start();
	}
}

class ClassInit {
	private static ClassInit ci = init();

	private static ClassInit init() {
		if (true) {
			throw new RuntimeException();
		}
		return null;

	}

	public static void test() {

	}
}

 

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics