论坛首页 Java企业应用论坛

再请教双重检查锁定DCL的问题

浏览 19686 次
精华帖 (0) :: 良好帖 (11) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-22  
详细解释一下:
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来搞:
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;
  }
}


最后,这个是java 5新加的,以前是没有的。
0 请登录后投票
   发表时间:2008-01-23  
多谢各位。

偶仔细看看资料先。
0 请登录后投票
   发表时间:2008-01-23  
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来搞:
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;
  }
}


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


继续讨论一下,你说x86上没有实现volatile的memory barrier语义不知道有什么根据,jdk5修改了memory model后主要的一个增强就是对volatile变量的access order加强了约束,如果x86上没有实现memory barrier是指5.0之前还是一直没有,如果5.0之后还没有只能说它不符合jvm的规范了,我不太相信。

这个double checking算法如果不用volatile是错误的,如果没有volatile,第二个checking可能会被jit编译器忽略掉。
0 请登录后投票
   发表时间:2008-01-23  
tinywind 写道
neuzhujf 写道
学习了,以前没注意到。
下面这样是不是也应该没有问题?
public class LazySingleton {
private static LazySingleton m_instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
	if (m_instance == null) {
		synchronized (LazySingleton.class) {
			if (m_instance == null) {
				LazySingleton instance = new LazySingleton();
				m_instance = instance;
			}
		}
	}
	return m_instance;
}
}



说实话,你的代码和lz的没有任何区别,原先有的错误还是存在。
简单的解决方案就是将m_instance声明为volatile,在JDK1.5之后没有问题。



没明白,LazySingleton instance = new LazySingleton();执行的时候,对象初期化,并且付值给本地变量。
这时实例变量还是null,并不会出现搂主说的其他线程使用没有初期化完的对象。
等这条语句执行完,那么对象初期化结束,然后把这个对象付值给实例变量。
不明白为什么还会有错误,望gs指教
0 请登录后投票
   发表时间:2008-01-23  
我在上一楼作过简单说明了,第二个checking会被忽略。

加入一个栈变量我不认为会改变什么,编译器优化完全可以忽略它。简化成m_instance=new LazySingleton()的形式,Hotspot的jit compiler优化作的相当好,如果没这样作我会觉得很奇怪。
0 请登录后投票
   发表时间:2008-01-23  
关于延迟初始化,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.
0 请登录后投票
   发表时间:2008-01-23  
DCL的替代Initialize-On-Demand:

public class Foo {
    // 似有静态内部类, 只有当有引用时, 该类才会被装载
    private static class LazyFoo {
       public static Foo foo = new Foo();
    }

    public static Foo getInstance() {
       return LazyFoo.foo;
    }
}
0 请登录后投票
   发表时间:2008-01-23  
楼上算比较tricky的方法,主要是利用jvm加载类时用的锁。
0 请登录后投票
   发表时间:2008-01-23  
galaxystar 写道
DCL的替代Initialize-On-Demand:

public class Foo {
    // 似有静态内部类, 只有当有引用时, 该类才会被装载
    private static class LazyFoo {
       public static Foo foo = new Foo();
    }

    public static Foo getInstance() {
       return LazyFoo.foo;
    }
}

这方法比自己写singleton要好得多,也很简洁。不过一般来说,如果Foo类只有一个getInstance()一个途径得到实例,那么甚至不需要额外的LazyFoo,就直接在Foo类里面private static fiinal Foo singleton = new Foo();就够了。更简单。

DCL更一般的意义上,不是用来做可以用static 变量实现的singleton的(singleton有你提到的这种更好的方法,甚至更好的是不要做singleton,而是用依赖注射),而是在一个instance里面实现延迟加载。


0 请登录后投票
   发表时间:2008-01-23  
tinywind 写道
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来搞:
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;
  }
}


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


继续讨论一下,你说x86上没有实现volatile的memory barrier语义不知道有什么根据,jdk5修改了memory model后主要的一个增强就是对volatile变量的access order加强了约束,如果x86上没有实现memory barrier是指5.0之前还是一直没有,如果5.0之后还没有只能说它不符合jvm的规范了,我不太相信。

这个double checking算法如果不用volatile是错误的,如果没有volatile,第二个checking可能会被jit编译器忽略掉。


我找不到确切的证据,我也没有自己真正去验证过。我只是选择相信告诉我这个事情的那位牛牛的话(上面那篇论文的作者)。总的来说,目前为止,依赖于volatile的memory barrier语义还是一个危险的看人品的决定。
0 请登录后投票
论坛首页 Java企业应用版

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