[英]Pattern for lazy thread-safe singleton instantiation in java
懶惰的線程安全單例實例對每個編碼器來說都不容易理解,所以我想在我們的企業框架中創建一個可以完成這項工作的類。
你怎么看待這件事? 你覺得它有什么壞處嗎? 在Apache Commons中有類似的東西嗎? 我怎樣才能讓它變得更好?
Supplier.java
public interface Supplier<T> {
public T get();
}
LazyThreadSafeInstantiator.java
public class LazyThreadSafeInstantiator<T> implements Supplier<T> {
private final Supplier<T> instanceSupplier;
private volatile T obj;
public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) {
this.instanceSupplier = instanceSupplier;
}
@Override
// http://en.wikipedia.org/wiki/Double-checked_locking
public T get() {
T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt.
if (result == null) {
synchronized(this) {
result = obj;
if (result == null) {
result = instanceSupplier.get();
obj = result;
}
}
}
return result;
}
}
用法示例:
public class Singleton1 {
private static final Supplier<Singleton1> instanceHolder =
new LazyThreadSafeInstantiator<Singleton1>(new Supplier<Singleton1>() {
@Override
public Singleton1 get() {
return new Singleton1();
}
});
public Singleton1 instance() {
return instanceHolder.get();
}
private Singleton1() {
System.out.println("Singleton1 instantiated");
}
}
謝謝
懶惰的線程安全單例實例對每個編碼器來說都不容易理解
不,它實際上非常非常容易:
public class Singleton{
private final static Singleton instance = new Singleton();
private Singleton(){ ... }
public static Singleton getInstance(){ return instance; }
}
更好的是,讓它成為一個枚舉:
public enum Singleton{
INSTANCE;
private Singleton(){ ... }
}
它是線程安全的,它是懶惰的(初始化發生在類加載時,Java不會加載類,直到它們被首次引用)。
事實上,99%的時候你根本不需要延遲加載 。 剩下的1%,在0.9%以上,完全是懶惰的。
您是否運行過探查器並確定您的應用程序屬於真正需要首先訪問延遲加載的0.01%? 不這么認為。 那你為什么要浪費時間來編造這些Rube Goldbergesque代碼可惡來解決一個不存在的問題呢?
對於比問題中提供的更具可讀性(在我看來)的版本,可以參考Bill Pugh介紹的Initialization on Demand Holder慣用法 。 考慮到Java 5內存模型,它不僅是線程安全的,單例也被懶惰地初始化。
由於Java內存模型和亂序執行的可能性,是不是雙重檢查鎖定模式和在JIT編譯器和多核/處理器系統上使用volatile 破壞 ?
更一般地說,似乎單身人士的框架對於本質上非常簡單的正確實現模式來說是過度的。
看起來過度工程對我來說。
我真的不知道助手班有多幫助 。
首先,它使用雙鎖定語,並且已經被證明一次又一次被破壞。
第二,如果你必須使用單,為什么不初始化static final
實例。
public class Singleton1 {
private static final Singleton1 instanceHolder =
new Singletong1( );
public Singleton1 instance() {
return instanceHolder;
}
private Singleton1() {
System.out.println("Singleton1 instantiated");
}
}
此代碼是線程安全的,並且已經證明可以正常工作。
查看Vineet Reynolds的答案,了解何時需要在第一次獲取時初始化單例實例。 在許多情況下,我認為這種做法也是一種過度殺傷力。
我同意其他海報,並說這看起來有點矯枉過正,但是我說我確實認為這是初級開發人員可能出錯的事情。 我認為,因為構造單例的供應商的行為(如下所示)幾乎在所有情況下都是相同的,我很想將它作為默認行為放在LazyThreadSafeInstantiator
。 每次想要使用單例時使用自治內部類都非常混亂。
@Override
public Singleton1 get() {
return new Singleton1();
}
這可以通過提供一個重載的構造函數來完成,該構造函數將Class轉換為所需的單例。
public class LazyThreadSafeInstantiator<T> implements Supplier<T> {
private final Supplier<T> instanceSupplier;
private Class<T> toConstruct;
private volatile T obj;
public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) {
this.instanceSupplier = instanceSupplier;
}
public LazyThreadSafeInstantiator(Class<t> toConstruct) {
this.toConstruct = toConstruct;
}
@Override
// http://en.wikipedia.org/wiki/Double-checked_locking
public T get() {
T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt.
if (result == null) {
synchronized(this) {
result = obj;
if (result == null) {
if (instanceSupplier == null) {
try {
Constructor[] c = toConstruct.getDeclaredConstructors();
c[0].setAccessible(true);
result = c[0].newInstance(new Object[] {});
} catch (Exception e) {
//handle
}
result =
} else {
result = instanceSupplier.get();
}
obj = result;
}
}
}
return result;
}
}
然后就可以這樣使用。
private static final Supplier<Singleton1> instanceHolder =
new LazyThreadSafeInstantiator<Singleton1>(Singleton1.getClass());
這是我的意見有點清潔。 您可以進一步擴展它以使用構造函數參數。
Lazy<X> lazyX= new Lazy<X>(){
protected X create(){
return new X();
}};
X x = lazyX.get();
abstract public class Lazy<T>
{
abstract protected T create();
static class FinalRef<S>
{
final S value;
FinalRef(S value){ this.value =value; }
}
FinalRef<T> ref = null;
public T get()
{
FinalRef<T> result = ref;
if(result==null)
{
synchronized(this)
{
if(ref==null)
ref = new FinalRef<T>( create() );
result = ref;
}
}
return result.value;
}
}
除了線程中的第一個get(),所有get()調用都不需要同步或volatile讀取。 實現了雙重檢查鎖定的最初目標。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.