java中延迟线程安全单例实例化的模式

时间:2010-09-03 11:48:08

标签: java thread-safety singleton instantiation lazy-evaluation

懒惰的线程安全单例实例对每个编码器都不容易理解,所以我想在我们的企业框架中创建一个可以完成这项工作的类。

你怎么看?你觉得它有什么坏处吗?在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");
    }
}

由于

6 个答案:

答案 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读取。实现了双重检查锁定的最初目标。