线程安全在Java中实现单例模式的有效方法?
可能重复:
Efficient way to implement singleton pattern in Java线程安全在Java中实现单例模式的有效方法?
我读这个Best Singleton Implementation In Java,但它不是线程安全的。
按照维基:
if(singleton==null) { synchronized(Singleton.class) { // this is needed if two threads are waiting at the monitor at the // time when singleton was getting instantiated if(singleton==null) singleton= new Singleton(); }
}
,但发现错误效用给出了这样两个错误: 1.双击空检查。 2.静态字段的延迟初始化不正确。
什么是最好的方式,
是否正确:
synchronized (Singleton.class) { if (singleton== null) { singleton= new Singleton(); } }
最有效的/最简单的方法,使延迟加载辛格尔顿只是
enum Singleton {
INSTANCE
}
注意:因为类加载是线程安全的,所以不需要锁定。该类默认是final,并且构造函数不能通过反射调用。 INSTANCE不会被创建,直到INSTANCE或该类被使用。如果您担心课程可能会被意外使用,您可以将单身人士包装在内部课程中。
final class Singleton {
private Singleton() { }
static class SingletonHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
恕我直言,你必须是相当偏执,认为这是一个更好的解决方案。
在你的第二个代码中,你没有使用'enum',但是你正在使用'class',所以我很困惑如何使用它。我想创建一个枚举来初始化一个类,并在下一次调用该对象。我该怎么做。请给出一个例子详细说明 – manish 2014-02-02 10:31:11
@Manish在这种情况下,使用'enum'就像我在第一个例子。 – 2014-02-02 10:32:47
嗯,没问题,但是我怎样才能初始化一个类,并在下一次使用'enum'时使用这个实例。这是我的困惑。每一个我只看到'枚举Singleton { INSTANCE }这么多,但不是如何调用和初始化对象 – manish 2014-02-02 10:35:12
Efficient way to implement singleton pattern in Java的接受答案中的第一个代码示例是线程安全。第一次加载类时,类加载器将执行INSTANCE
的创建;它精确的进行一次,并在一个线程安全的方式:
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
}
(从What is an efficient way to implement a singleton pattern in Java?复制)
在问题中的第二个代码示例是正确的,线程安全的,但它会导致在每次调用同步到getInstance()
,这会影响性能。
不保护私有构造函数是相当偏执的。我假设它避免使用反射创建另一个实例,或者它是否阻止调用构造函数的内部类? – 2010-12-19 10:48:55
我正在讨论上一个问题的答案,所以我照原样复制了代码。我个人会忽略'if(INSTANCE!= null)'检查。 – 2010-12-19 10:52:07
并因此我假设有例外情况。 ;) – 2010-12-19 13:05:41
关于这个问题已经写了很多。是的,简单的双重检查锁定模式并不安全。但是,通过将静态实例声明为volatile,可以使其安全。新的Java内存模型规范在处理volatile时为编译器添加了一些代码重新排序限制,因此原始风险已经消失。
反正我在创建实例时,很少真正需要这种lazyness的,所以我通常只是在类加载时静态创建它:
private static MyClass instance = new MyClass();
这是短暂的,明确的。作为替代方案,如果你真的想使懒惰,你可以利用的类加载的特性,做这样的:
public class MyClass {
private static class MyClassInit {
public static final MyClass instance = new MyClass();
}
public static MyClass getInstance() {
return MyClassInit.instance;
}
...
}
嵌套类不会被()第一次加载直到调用的getInstance。
这是上述问题的重复;看到这个问题的细节。但我不确定这个问题是否有这些有用的链接,因此:[关于Java中的双重检查锁定](http://www.ibm.com/developerworks/java/library/j-dcl.html)链接到[这些](http://www.ibm.com/developerworks/library/j-jtp02244.html)[two](http://www.ibm.com/developerworks/library/j-jtp03304/)针对Java的更新另见[维基百科关于双重锁定的文章](http://en.wikipedia.org/wiki/Double-checked_locking)。但是对于您的问题的实际答案,请参阅上面链接的问题。 – 2010-12-19 10:52:02