懒惰的线程安全单例实例对每个编码器都不容易理解,所以我想在我们的企业框架中创建一个可以完成这项工作的类。
你怎么看?你觉得它有什么坏处吗?在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");
}
}
由于
答案 0 :(得分:55)
懒惰的线程安全单例 实习有点不容易 了解每个程序员
不,它实际上非常非常容易:
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代码可恶来解决一个不存在的问题呢?
答案 1 :(得分:5)
对于比问题中提供的更具可读性(在我看来)的版本,可以参考Bill Pugh介绍的Initialization on Demand Holder idiom。考虑到Java 5内存模型,它不仅是线程安全的,单例也是懒惰地初始化。
答案 2 :(得分:3)
由于Java内存模型&amp;而不是JIT编译器和多核/处理器系统上的双重检查锁定模式和易失性broken的使用。乱序执行的可能性?
更一般地说,单身人士的框架似乎过于苛刻,因为这本质上是一种非常直接的模式,无法正确实施。
答案 3 :(得分:3)
看起来对我过度工作。
我真的不知道如何帮助类帮助。
首先,它正在使用双锁习语,并且它已被证明一次又一次被破坏。
其次,如果必须使用单例,为什么不初始化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的答案,了解何时需要在第一个 get 上初始化单例实例。在许多情况下,我认为这种做法也是一种过度杀伤。
答案 4 :(得分:0)
我同意其他海报并说这看起来有点矫枉过正,但是我说我确实认为这是初级开发人员可能出错的事情。我认为,因为构建单例的供应商(如下所示)的行为几乎在所有情况下都是相同的,我很想把它作为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());
这是我的意见有点清洁。您可以进一步扩展它以使用构造函数参数。
答案 5 :(得分:0)
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读取。实现了双重检查锁定的最初目标。