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
沒有留言:
張貼留言