线程安全在Java中实现单例模式的有效方法? [复制]
Posted
技术标签:
【中文标题】线程安全在Java中实现单例模式的有效方法? [复制]【英文标题】:Thread Safe Efficient way to implement singleton pattern in Java? [duplicate] 【发布时间】:2011-05-27 19:42:06 【问题描述】:可能重复:Efficient way to implement singleton pattern in 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();
但是 Find Bugs 实用程序在这方面给出了两个错误: 1.双重空检查。 2. 静态字段延迟初始化不正确。
什么是最好的方法,
是否正确:
synchronized (Singleton.class) if (singleton== null) singleton= new Singleton();
【问题讨论】:
这是上述问题的重复;有关详细信息,请参阅该问题。但我不确定这个问题是否有这些有用的链接,所以:About double-checked locking in Java 链接到 these two Java 5 更新。另请参阅 Wikipedia's article on double-checked locking。但对于您的问题的实际答案,请参阅上面链接的问题。 【参考方案1】:制作延迟加载单例的最有效/最简单的方法就是
enum Singleton
INSTANCE
注意:不需要锁定,因为类加载是线程安全的。该类默认为final,不能通过反射调用构造函数。在使用实例或使用类之前,不会创建实例。如果您担心该类可能会被意外使用,您可以将单例包装在内部类中。
final class Singleton
private Singleton()
static class SingletonHolder
static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance()
return SingletonHolder.INSTANCE;
恕我直言,您必须非常偏执才能认为这是一个更好的解决方案。
【讨论】:
在您的第二个代码中,您没有使用enum
,但您使用的是class
,所以我很困惑如何使用它。我想做一个枚举来初始化一个类,下次每次调用那个对象。我该怎么做。你能详细举个例子吗
@Manish 在这种情况下,使用enum
,就像我在第一个示例中所做的那样。
是的,没关系,但我怎样才能只初始化一个类并在下次使用enum
时使用该实例。那是我的困惑。我在哪里看到enum Singleton INSTANCE
这么多,但不知道如何调用和初始化对象
您可以在代码中的任何位置使用Singleton.INSTANCE
。以线程安全的方式调用一次的用于初始化此实例的代码将位于 enum
的构造函数中
@PeterLawrey 你没有忘记你的代码 sn-p 中的私有构造函数 Singleton() 吗?【参考方案2】:
关于这个问题已经写了很多。是的,简单的双重检查锁定模式并不安全。但是您可以通过将静态实例声明为 volatile 来使其安全。新的 Java 内存模型规范在处理 volatile 时为编译器添加了一些代码重新排序限制,因此原来的风险已经消失。
反正我在创建实例时很少真正需要这种惰性,所以我通常只是在类加载时静态地创建它:
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() 之前,不会加载嵌套类。
【讨论】:
【参考方案3】: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()
时同步,从而影响性能。
【讨论】:
不保护私有构造函数是相当偏执的。我假设它避免使用反射创建另一个实例,还是停止内部类调用构造函数? 我正在讨论上一个问题的答案,所以我按原样复制了代码。我个人也会省略if (INSTANCE != null)
检查。
因此我认为是例外。 ;)
您好 Eli,从您的回答中我了解到 Foo 对象将只有一个副本。但是假设两个线程同时调用 getInstance() 函数。那么我相信会有一个比赛条件。如何避免这种情况。因此我们需要避免这种情况。请查看 [link]***.com/questions/15930633/… 以获得最终答案...以上是关于线程安全在Java中实现单例模式的有效方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章