在Java中编写单例的不同方法

在java中编写单例的经典之处如下:

public class SingletonObject { private SingletonObject() { } public static SingletonObject getSingletonObject() { if (ref == null) // it's ok, we can call this constructor ref = new SingletonObject(); return ref; } private static SingletonObject ref; } 

如果我们需要在multithreading情况下运行,我们可以添加synchronized关键字。

但我更喜欢把它写成:

 public class SingletonObject { private SingletonObject() { // no code req'd } public static SingletonObject getSingletonObject() { return ref; } private static SingletonObject ref = new SingletonObject(); } 

我认为这更简洁,但奇怪的是我没有看到以这种方式编写的示例代码,如果我以这种方式编写代码会有什么不良影响吗?

您的代码与“示例代码”之间的区别在于,在加载类时会对您的单例进行实例化,而在“示例”版本中,在实际需要之前不会对其进行实例化。

在第二种forms中,你的单例被急切地加载,这实际上是首选forms(并且第一种forms不是线程安全的,因为你自己提到它)。 渴望加载对于生产代码来说并不是一件坏事,但是有些情况下你可能想要懒惰地加载你的单身,正如Guice的作者Bob Lee所说,我在下面引用的Lazy Loading Singletons :

首先,为什么要延迟加载单例? 在生产中,您通常希望急切地加载所有单例,以便及早发现错误并预先考虑任何性能,但在测试和开发过程中,您只想加载您绝对需要的内容,以免浪费时间。

在Java 1.5之前,我使用普通的旧同步懒惰加载单例,简单但有效:

 static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) instance == new Singleton(); return instance; } 

1.5中对内存模型的更改启用了臭名昭着的双重检查锁定(DCL)习语。 要实现DCL,请检查公共路径中的volatile字段,并仅在必要时进行同步:

 static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) instance == new Singleton(); } } return instance; } 

但是volatile并不比synchronized快得多, synchronized现在非常快,DCL需要更多的代码,所以即使在1.5出现之后,我继续使用普通的旧同步。

想象一下,今天我惊讶的是Jeremy Manson向我指出了需求初始化(IODH)习语 ,它需要很少的代码并且没有同步开销。 零,因为比volatile更快。 IODH需要与普通旧同步相同数量的代码行,并且它比DCL更快!

IODH使用惰性类初始化。 在您真正触摸类中的某些内容之前,JVM不会执行类的静态初始化程序。 这也适用于静态嵌套类。 在以下示例中, JLS保证 JVM在有人调用getInstance()之前不会初始化instance

 static class SingletonHolder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } 

[…]

更新:信用到期的信用, Effective Java (版权所有2001)详细说明了第48项下的这种模式。它继续指出您仍然必须在非静态上下文中使用同步或DCL。

我还在我的框架中将单例处理从同步切换到DCL,并且看到了另外10%的性能提升(与之前我开始使用cglib的快速reflection相比)。 我只在我的微基准测试中使用了一个线程,因此我使用相对精细的易失性字段访问替换了具有相当细粒度的易扩锁的锁,因此对并发性的提升可能更大。

请注意,Joshua Bloch现在建议(自Effective Java,第2版以来)使用Jonik指出的单元素enum来实现单例。

好吧,在后一种情况下,单例对象会在需要之前创建,但在大多数情况下,这可能并不是非常糟糕。

顺便说一句,Joshua Bloch建议(在Effective Java ,第2版,第3项中)使用单元素枚举实现单例:

 public enum SingletonObject { INSTANCE; } 

他给出了以下理由:

[…]它更简洁,免费提供序列化机制,并提供防止多个实例化的铁定保证,即使面对复杂的序列化或reflection攻击。 虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方法。

我会说后一个代码实际上是更标准的模式。 您的第一个版本不是线程安全的。 使其成为线程安全的方法包括在每次访问时进行同步,或者非常小心地使用双重检查锁定(这在Java 5内存模型中是安全的,只要你做对了)。

请注意,由于类被懒惰地初始化,如果您在类上调用静态方法而不想创建实例,则后一个代码仍然只会不必要地创建一个对象。

有一个模式使用嵌套类进行初始化,这可以使这个更懒,但个人第二种forms几乎总是对我自己做得足够好。

在Effective Java中有更多详细信息,但是我没有找到项目编号,我很害怕。

我认为你的问题是你正在混合单例和惰性初始化。 单例可以使用不同的初始化策略实现

  • 类加载初始化
  • 使用双重检查锁定的延迟初始化
  • 单次检查延迟初始化(可能重复初始化)
  • 使用类加载器的延迟初始化(holder class idiom)

所有这些方法都在Effective Java 2nd Item 71中进行了讨论:明智地使用延迟初始化 。

在java中实现单例模式的不同方法如下

 public class SingletonClass { private SingletonClass= null; public static SingletonClass getInstance() { if(SingletonClass != null) { SingletonClass = new SingletonClass(); } } } 

这不是线程安全的。 以下是单例设计模式的thread safe实现

1.严厉的同步

 private static YourObject instance; public static synchronized YourObject getInstance() { if (instance == null) { instance = new YourObject(); } return instance; } 

2.双重检查同步

参考

 private static final Object lock = new Object(); private static volatile YourObject instance; public static YourObject getInstance() { YourObject r = instance; if (r == null) { synchronized (lock) { // While we were waiting for the lock, another r = instance; // thread may have instantiated the object. if (r == null) { r = new YourObject(); instance = r; } } } return r; } 

3.按需初始化持有人习语

参考

 public class Something { private Something() {} private static class LazyHolder { static final Something INSTANCE = new Something(); } public static Something getInstance() { return LazyHolder.INSTANCE; } } 

其他人正在使用枚举

 public enum Singleton { SINGLE; public void myMethod(){ } } 

第二种方法将解决multithreading问题,但即使您不需要它也将始终创建Singleton。 最好的方法是创建一个嵌套类。

 public class singleton { private singleton() { System.out.println("I'am called only when it's needed"); } static class Nested { Nested() {} private static final singleton instance = new singleton(); } public static singleton getInstance() { return Nested.instance; } public static void main(String [] args) { singleton.getInstance(); } } 

编写单例类的最佳方法如下所示。请尝试一下

 public final class SingeltonTest { /** * @param args * @return */ private static SingeltonTest instance = null; private SingeltonTest() { System.out.println("Rahul Tripathi"); } public static SingeltonTest getInstance() { if (instance == null) { synchronized (SingeltonTest.class) { if (instance == null) instance == new SingeltonTest(); } } return instance; } } 
 // Lazy loading enabled as well as thread safe 

class Singleton {

  private static class SingletonHolder { public static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } } 

我同意Anon,并且在我总是想要实例化我将使用的单例的情况下

 public class SingletonObject { public static SingletonObject REF = new SingletonObject(); private SingletonObject() { // no code req'd } 

}