wiki裡面的例子, 基本的singleton 實作, 在單thread下
class Foo {
private Helper helper = null;
public synchronized Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}
// other functions and members...
}
但是這個在multi-thread下是不行的, 所以會有一種解決方式:
// Correct multithreaded version
class Foo {
private Helper helper = null;
public synchronized Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}
// other functions and members...
}
但是這種寫法效率上又不是很好,所以會有另一種的改進方法
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}
但是文章中又提到,上面這種看起來是work的,但是實際上是行不通的,因為new Helper()在很多平台和優化編譯器上是錯誤的。原因在於:helper = new Helper()這行代碼在不同編譯器上的行為是無法預知的。
看看另一個例子,
public class LazySingleton {
private int someField;
private static LazySingleton instance;
private LazySingleton() {
this .someField = new Random().nextInt( 200 ) 1 ;
}
public static LazySingleton getInstance() {
if (instance == null ) {
synchronized (LazySingleton. class ) {
if (instance == null ) {
instance = new LazySingleton();
}
}
}
return instance;
}
public int getSomeField() {
return this .someField;
}
}
這個例子下, 儘管得到了LazySingleton的正確的引用,但是卻有可能訪問到其成員變量的不正確值,具體來說LazySingleton.getInstance().getSomeField()有可能返回someField的默認值0。如果程序行為正確的話,這應當是不可能發生的事,因為在構造函數里設置的someField的值不可能為0。也說明這種情況理論上有可能發生。DCL的分析也告訴我們一條經驗原則, 對引用(包括對象引用和數組引用)的非同步訪問,即使得到該引用的最新值,卻並不能保證也能得到其成員變量(對數組而言就是每個數組元素)的最新值。LazySingleton是一個不變class,它只有get方法而沒有set方法,即使對於不可變對象,它也必須被安全的發布,才能被安全地共享。 所謂“安全的共享”就是說不需要同步也不會遇到數據競爭的問題。 在Java5或以後,將someField聲明成final的,即使它不被安全的發布,也能被安全地共享,而在Java1.4或以前則必須被安全地發布。
這種設計上的問題要如何解決?
在JDK 1.5裡面,我們可以這樣做
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null)
helper = result = new Helper();
}
}
return result;
}
// other functions and members...
}
將object 設為volatile , 注意volatile只能保證多線程的內存可見性,不能保證多線程的執行有序性。以下的a=a+count包含很多動作, 多個線程的執行是無序的,因為沒有任何機制來保證多個線程的執行有序性和原子性。 volatile存在的意義是,任何線程對a的修改,都會馬上被其他線程讀取到
public class VolatileTest{
public volatile int a;
public void add( int count){
a=a+count;
//a++;
}
}
在effective中提到的,簡單實現的方法public class Singleton {
private Singleton() {}
// Lazy initialization holder class idiom for static fields
private static class InstanceHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getSingleton() {
return InstanceHolder.instance;
}
}
或著實作threadlocal
class Foo {
/** If perThreadInstance.get() returns a non-null value, this thread
has done synchronization needed to see initialization
of helper */
private final ThreadLocal perThreadInstance = new ThreadLocal();
private Helper helper = null;
public Helper getHelper() {
if (perThreadInstance.get() == null) createHelper();
return helper;
}
private final void createHelper() {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
// Any non-null value would do as the argument here
perThreadInstance.set(perThreadInstance);
}
}
wiki 上的另一個例子,設成靜態的
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
一樣是使用volatile 跟 final , (JDK 1.5 對volatile & final 有加強一些語意,針對JMM)
public class FinalWrapper<T>
{
public final T value;
public FinalWrapper(T value) { this.value = value; }
}
public class Foo
{
private FinalWrapper<Helper> helperWrapper = null;
public Helper getHelper()
{
FinalWrapper<Helper> wrapper = helperWrapper;
if (wrapper == null)
{
synchronized(this)
{
if (helperWrapper ==null)
helperWrapper = new FinalWrapper<Helper>( new Helper() );
wrapper = helperWrapper;
}
}
return wrapper.value;
}
}
More infomation :
Performance comparision between ConcurrentHashMap and synchronized HashMap in Terracotta
ConcurrentHashMap
Wikipedia : Double-checked locking
The "Double-Checked Locking is Broken" Declaration
Jeremy Manson Blog:Double Checked Locking
用happen-before規則重新審視DCL
沒有留言:
張貼留言