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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1879283/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:28:51  来源:igfitidea点击:

Different ways to write singleton in Java

javasingleton

提问by gleery

The classic of writing a singleton in java is like this:

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

and we can add synchronized keyword if we need it to run in multithreaded cases.

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

But I prefer to write it as:

但我更喜欢把它写成:

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

    public static SingletonObject getSingletonObject()
    {
      return ref;
    }

    private static SingletonObject ref = new SingletonObject();
}

which I think is more concise, but strangely I didn't see any sample code written in this way, is there any bad effects if I wrote my code in this way?

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

采纳答案by Anon.

The difference between your code and the "sample code" is that your singleton is instantiated when the class is loaded, while in the "sample" version, it is not instantiated until it is actually needed.

您的代码和“示例代码”之间的区别在于,您的单例在类加载时被实例化,而在“示例”版本中,它直到实际需要时才被实例化。

回答by Jonik

Well, in the latter case the singleton object gets created before it is ever needed, but in most cases that's probably not horribly bad.

好吧,在后一种情况下,单例对象在它被需要之前就被创建了,但在大多数情况下,这可能不是很糟糕。

By the way, Joshua Bloch recommends (in Effective Java, 2nd ed, item 3) implementing singletons using a single-element enum:

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

public enum SingletonObject { 
    INSTANCE;
}

He gives the following justification:

他给出了以下理由:

[...] it is more concise, provides serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

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

回答by Jon Skeet

I would say the latter code is the more standard pattern, actually. Your first version isn't thread-safe. Ways of making it thread-safe include synchronizing on every access, or very carefullymaking it use double-checked locking (which is safe as of the Java 5 memory model, so long as you get it right).

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

Note that due to classes being initialized lazily, your latter code would still only create an object unnecessarily if you called static methods on the class without wanting to create the instance.

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

There's a pattern using a nested class to do the initialization which can make this lazier, but personally the second form almost always does well enough for me on its own.

有一种使用嵌套类进行初始化的模式,这可以使这个更懒惰,但就个人而言,第二种形式几乎总是对我来说足够好。

There are more details of this in Effective Java, but I don't have it with me to find the item number, I'm afraid.

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

回答by Thomas Jung

I think your problem is that you're mixing singleton and lazy initialization. A singleton can be implemented with different initialization strategies:

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

  • initialization on class loading
  • lazy initialization that uses double checked locking
  • lazy initialization with single checking (with possible repeated initialization)
  • lazy initialization that uses the class loader (holder class idiom)
  • 类加载初始化
  • 使用双重检查锁定的延迟初始化
  • 单次检查的延迟初始化(可能重复初始化)
  • 使用类加载器的延迟初始化(持有者类习语)

All these approaches are discussed in Effective Java 2nd Item 71: Use lazy initialization judiciously.

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

回答by Steve De Caux

I agree with Anon, and in the case where I always want to instantiate the singleton I would use

我同意 Anon,在我总是想实例化单例的情况下,我会使用

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

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

}

}

回答by Pascal Thivent

In the second form, your singleton is eagerlyloaded and this is actually the preferred form (and the first one isn't thread-safe as you mentioned it yourself). Eager loading is not a bad thing for production code but there are contexts where you might want to lazy load your singletons, as discussed by the author of Guice, Bob Lee, in Lazy Loading Singletonsthat I'm quoting below:

在第二种形式中,你的单例被急切加载,这实际上是首选形式(第一个不是你自己提到的线程安全的)。急切加载对于生产代码来说并不是一件坏事,但在某些情况下,您可能希望延迟加载单例,正如 Guice 的作者 Bob Lee 在我在下面引用的Lazy Loading Singletons中所讨论的:

First, why would you want to lazy load a singleton? In production, you typically want to eagerly load all your singletons so you catch errors early and take any performance hit up front, but in tests and during development, you only want to load what you absolutely need so as not to waste time.

Before Java 1.5, I lazy loaded singletons using plain old synchronization, simple but effective:

static Singleton instance;

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

Changes to the memory model in 1.5 enabled the infamous Double-Checked Locking (DCL) idiom. To implement DCL, you check a volatilefield in the common path and only synchronize when necessary:

static volatile Singleton instance;

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

But volatileisn't that much faster than synchronized, synchronizedis pretty fast nowadays, and DCL requires more code, so even after 1.5 came out, I continued using plain old synchronization.

Imagine my surprise today when Jeremy Manson pointed me to the Initialization on Demand Holder (IODH) idiomwhich requires very little code and has zero synchronization overhead. Zero, as in even faster than volatile. IODH requires the same number of lines of code as plain old synchronization, and it's faster than DCL!

IODH utilizes lazy class initialization. The JVM won't execute a class's static initializer until you actually touch something in the class. This applies to static nested classes, too. In the following example, the JLS guaranteesthe JVM will not initialize instanceuntil someone calls getInstance():

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

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

[...]

Update:Credit where credit is due, Effective Java(copyright 2001) detailed this pattern under item 48. It goes on to point out that you still have to use synchronization or DCL in non-static contexts.

I also switched singleton handling in my framework from synchronization to DCL and saw another 10% performance boost (compared to before I started using cglib's fast reflection). I only used one thread in my micro-benchmark, so the boost to concurrency could be even greater given that I replaced a heavily contended lock with a relatively fine grained volatile field access.

首先,为什么要延迟加载单例?在生产中,您通常希望立即加载所有单例,以便及早发现错误并提前解决任何性能问题,但在测试和开发期间,您只想加载绝对需要的内容,以免浪费时间。

在 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并没有比 快多少synchronizedsynchronized现在已经相当快了,而且 DCL 需要更多的代码,所以即使在 1.5 出来之后,我还是继续使用普通的旧同步。

想象一下,当 Jeremy Manson 向我指出按需初始化 (IODH) 习惯用法时,我感到惊讶的是, 它只需要很少的代码并且具有零同步开销。零,因为甚至比volatile. IODH 需要的代码行数与普通的旧同步相同,而且比 DCL 快!

IODH 使用惰性类初始化。JVM 不会执行类的静态初始值设定项,直到您实际接触类中的某些内容。这也适用于静态嵌套类。在以下示例中, JLS 保证JVM 不会instance在有人调用 之前初始化getInstance()

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

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

[...]

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

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

Note that Joshua Bloch now recommends (since Effective Java, 2nd ed) to implement singletons using a single-element enumas pointed out by Jonik.

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

回答by Belaid Khaled

the second way will solve multi-threading issue but will always create the Singleton even when you don't need it. The best way to do it is to create a Nested class.

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

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

回答by Rahul Tripathi

The best way to write a singleton class is given below .Please try it

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

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

回答by Tom Taylor

Different ways to implement singleton patternin java is as follows

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

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

This is not thread safe. The following are thread safeimplementation of singleton design pattern

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

1. Draconian synchronization

1.严酷的同步

private static YourObject instance;

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

2.Double check synchronization

2.双校验同步

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. Initialization-on-demand holder idiom

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. Other one is using enum

4. 另一个是使用枚举

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

回答by Nandan

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