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

时间:2009-12-10 07:46:11

标签: java singleton

在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;
}

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

但我更喜欢把它写成:

public class SingletonObject
{
    private SingletonObject()
    {
        // no code req'd
    }

    public static SingletonObject getSingletonObject()
    {
      return ref;
    }

    private static SingletonObject ref = new SingletonObject();
}

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

11 个答案:

答案 0 :(得分:18)

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

答案 1 :(得分:18)

在第二种形式中,你的单身人士急切地加载,这实际上是首选形式(并且第一种不是线程安全的,因为你自己提到它)。渴望加载对于生产代码来说并不是一件坏事,但是有些情况下你可能想要懒惰地加载你的单身,正如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中对内存模型的更改   启用了臭名昭着的Double-Checked   锁定(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并没有那么快   比synchronizedsynchronized   现在非常快,DCL需要   更多的代码,所以即使1.5出来后,   我继续使用普通的旧   同步。

     

想象一下杰里米今天的惊喜   曼森指着我   Initialization on Demand Holder (IODH) idiom这非常需要   小代码,零   同步开销。零,如在   甚至比volatile更快。 IODH   需要相同数量的行   代码作为普通的旧同步,和   它比DCL快!

     

IODH使用懒惰类   初始化。 JVM将不会执行   一个类的静态初始化器,直到你   实际上在课堂上触摸一些东西。   这适用于静态嵌套类,   太。在以下示例中,   JLS guarantees JVM不会   初始化instance直到有人   来电getInstance()

static class SingletonHolder {
  static Singleton instance = new Singleton();    
}

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

[...]

     

更新:信用到期的信用,Effective Java(版权所有   2001)在项目下详述了这种模式   它继续指出你仍然需要使用同步或   非静态上下文中的DCL。

     

我也改变了单身处理   我的框架从同步到   DCL又看到了10%的性能   提升(与我开始之前相比)   使用cglib的快速反射)。我只是   在我的微基准测试中使用了一个线程,   所以可以提升并发性   考虑到我取代了一个,甚至更大   与...争夺锁定   相对细小的挥发性油田   访问。

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

答案 2 :(得分:10)

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

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

public enum SingletonObject { 
    INSTANCE;
}

他给出了以下理由:

  

[...]它更简洁,提供序列化   机器免费,并提供   反对多重的铁定保证   实例化,即使面对   复杂的序列化或   反思攻击。虽然这个   方法尚未被广泛采用,   单元素枚举类型是最好的   实现单身人士的方法。

答案 3 :(得分:5)

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

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

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

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

答案 4 :(得分:2)

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

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

Effective Java 2nd Item 71: Use lazy initialization judiciously中讨论了所有这些方法。

答案 5 :(得分:2)

在java中实现singleton pattern的不同方法如下

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.Double check synchronization

Reference

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。初始化按需持有人习语

Reference

public class Something {
    private Something() {}

    private static class LazyHolder {
        static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

<强> 4。另一个是使用枚举

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

答案 6 :(得分:1)

第二种方式将解决多线程问题,但即使您不需要它也会始终创建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();
    }
}

答案 7 :(得分:0)

下面给出了编写单例类的最佳方法。请试一试

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;
    }
}

答案 8 :(得分:0)

// 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;
                  }
               }

答案 9 :(得分:0)

我声明了私有构造函数和静态块,因此静态块的目的是它只会执行一次,而在静态块内部我正在创建一个对象,因此构造函数将仅被调用一次。

Endpoint not reachable. Refresh cache and retry
Failover happening. retryCount 6
RefreshLocationAsync() refreshing locations
An error occurred while sending the request. ---> System.Net.WebException: The remote server returned an error: (407) Proxy Authentication Required.
Endpoint not reachable. Refresh cache and retry
Failover happening. retryCount 7
RefreshLocationAsync() refreshing locations
An error occurred while sending the request. ---> System.Net.WebException: The remote server returned an error: (407) Proxy Authentication Required.
....

}

答案 10 :(得分:-2)

我同意Anon,如果我总是想要实例化我会使用的单身人士

public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();

private SingletonObject()
{
    // no code req'd
}

}