java java中惰性线程安全单例实例化的模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3635396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pattern for lazy thread-safe singleton instantiation in java
提问by Igor Mukhin
the lazy thread-safe singleton instantion is kinda not easy to understand to every coder, so i wanted to create a class in our enterprise framework that would do the job.
懒惰的线程安全单例实例对于每个编码人员来说都不太容易理解,所以我想在我们的企业框架中创建一个可以完成这项工作的类。
What do you think about it? Do you see something bad about it? Is there something similar like in Apache Commons? How can i make it better?
你怎么看待这件事?你看到它有什么不好的吗?Apache Commons 中是否有类似的东西?我怎样才能让它更好?
Supplier.java
供应商.java
public interface Supplier<T> {
public T get();
}
LazyThreadSafeInstantiator.java
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;
}
}
Example usage:
用法示例:
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");
}
}
Thanks
谢谢
回答by Michael Borgwardt
the lazy thread-safe singleton instantion is kinda not easy to understand to every coder
懒惰的线程安全单例实例对每个编码人员来说都不太容易理解
No, it's actually very, very easy:
不,它实际上非常非常简单:
public class Singleton{
private final static Singleton instance = new Singleton();
private Singleton(){ ... }
public static Singleton getInstance(){ return instance; }
}
Better yet, make it an enum:
更好的是,使它成为一个枚举:
public enum Singleton{
INSTANCE;
private Singleton(){ ... }
}
It's threadsafe, and it's lazy (initialization happens at class loading time, and Java does not load classes until they are are first referred).
它是线程安全的,而且是惰性的(初始化发生在类加载时,Java 不会加载类,直到它们第一次被引用)。
Fact is, 99% of the time you don't need lazy loading at all. And out of the remaining 1%, in 0.9% the above is perfectly lazy enough.
事实是,99% 的情况下您根本不需要延迟加载。在剩下的 1% 中,上面的 0.9% 已经足够懒惰了。
Have you run a profiler and determined that your app belings to the 0.01% that really needs lazy-loading-at-first-access? Didn't think so. Then why are you wasting your time concocting these Rube Goldbergesque code abominations to solve a non-existing problem?
您是否运行了分析器并确定您的应用程序达到了真正需要首次访问延迟加载的 0.01%?没想到 那你为什么要浪费时间编造这些 Rube Goldbergesque 的可憎代码来解决一个不存在的问题?
回答by Vineet Reynolds
For a version that is more readable (in my opinion) than the one presented in the question, one can refer to the Initialization on Demand Holder idiom, introduced by Bill Pugh. Not only is it thread-safe considering the Java 5 memory model, the singleton is also lazily initialized.
对于比问题中提供的版本更具可读性(在我看来)的版本,可以参考Bill Pugh 介绍的Initialization on Demand Holder idiom。考虑到 Java 5 内存模型,它不仅是线程安全的,而且单例也被延迟初始化。
回答by Alexander Pogrebnyak
Looks overengineered to me.
对我来说看起来过度设计。
I really don't see how having helperclass helps.
我真的不明白有帮助类有什么帮助。
First of all, it's using double-lockingidiom, and it has been proved once and again broken.
首先,它使用了双重锁定的习语,并且已经被一次次地证明了。
Second, if you HAVE TOuse singleton, why not initialize static final
instance.
第二,如果你必须使用单,为什么不初始化static final
实例。
public class Singleton1 {
private static final Singleton1 instanceHolder =
new Singletong1( );
public Singleton1 instance() {
return instanceHolder;
}
private Singleton1() {
System.out.println("Singleton1 instantiated");
}
}
This code is thread-safe and has been proven to work.
此代码是线程安全的,并且已被证明可以工作。
Check Vineet Reynolds' answer for when you need to initialize singleton instance on a first get. In many cases I think that approach is an overkill as well.
检查 Vineet Reynolds 的答案,了解何时需要在第一次get时初始化单例实例。在许多情况下,我认为这种方法也是一种矫枉过正。
回答by Joel
Isn't the double checked locking pattern and use of volatile brokenon JIT compilers and multi-core/processor systems due to the Java Memory Model & possibility of out of order execution?
由于 Java 内存模型和乱序执行的可能性,在 JIT 编译器和多核/处理器系统上双重检查锁定模式和 volatile 的使用不是被破坏了吗?
More generally, it seems that a framework for singletons is overkill for what is essentially a pretty straightforward pattern to implement correctly.
更一般地说,对于本质上是正确实现的非常简单的模式来说,单例框架似乎是过度的。
回答by mR_fr0g
I would agree with other posters and say that this does seem like overkill, but have said that i do think that this is something that a junior developer is likely to get wrong. I think that because the behaviour of the supplier that constructs the singleton (shown below) is going to be the same in nearly all cases, i would be tempted to put this as default behaviour in the LazyThreadSafeInstantiator
. The use of the annonomous inner class every time you want to use a singleton is really messy.
我会同意其他海报并说这确实看起来有点矫枉过正,但我已经说过我确实认为这是初级开发人员可能会出错的事情。我认为因为构建单例的供应商的行为(如下所示)在几乎所有情况下都是相同的,我很想把它作为默认行为放在LazyThreadSafeInstantiator
. 每次要使用单例时都使用匿名内部类,真的很乱。
@Override
public Singleton1 get() {
return new Singleton1();
}
This could be done by providing an overloaded constructor that takes the Class to the singleton required.
这可以通过提供一个重载构造函数来完成,该构造函数将类带到所需的单例。
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;
}
}
This would then be used like so.
这将像这样使用。
private static final Supplier<Singleton1> instanceHolder =
new LazyThreadSafeInstantiator<Singleton1>(Singleton1.getClass());
This is my opinion is a bit cleaner. You could alos extend this further to use constructor arguments.
这是我的意见更清晰一点。您也可以进一步扩展它以使用构造函数参数。
回答by irreputable
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;
}
}
except maybe the first get() in a thread, all get() calls require no synchronization or volatile read. the original goal of double checked locking is achieved.
除了线程中的第一个 get(),所有 get() 调用都不需要同步或易失性读取。实现了双重检查锁定的最初目标。