Java 同步单例的正确使用?

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

proper usage of synchronized singleton?

javamultithreadingsingletonsynchronized

提问by Dan.StackOverflow

So I am thinking about building a hobby project, one off kind of thing, just to brush up on my programming/design.

所以我正在考虑建立一个业余爱好项目,一种非常规的事情,只是为了复习我的编程/设计。

It's basically a multi threaded web spider, updating the same data structure object->int.

它基本上是一个多线程的网络蜘蛛,更新相同的数据结构 object->int。

So it is definitely overkill to use a database for this, and the only thing I could think of is a thread-safe singleton used to contain my data structure. http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html

因此,为此使用数据库绝对是矫枉过正,我唯一能想到的是用于包含我的数据结构的线程安全单例。http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html

Is there a different approach I should look in to?

有没有我应该考虑的不同方法?

采纳答案by Steve Kuo

Double-checked locking has been proven to be incorrect and flawed (as least in Java). Do a search or look at Wikipedia's entryfor the exact reason.

双重检查锁定已被证明是不正确和有缺陷的(至少在 Java 中)。搜索或查看维基百科条目以了解确切原因。

First and foremost is program correctness. If your code is not thread-safe (in a multi-threaded environment) then it's broken. Correctness comes first before performance optimization.

首先是程序的正确性。如果您的代码不是线程安全的(在多线程环境中),那么它就被破坏了。在性能优化之前,正确性是第一位的。

To be correct you'll have to synchronize the whole getInstancemethod

为了正确,您必须同步整个getInstance方法

public static synchronized Singleton getInstance() {
   if (instance==null) ...
}

or statically initialize it

或静态初始化它

private static final Singleton INSTANCE = new Singleton();

回答by erickson

Using lazy initialization for the database in a web crawler is probably not worthwhile. Lazy initialization adds complexity and an ongoing speed hit. One case where it is justified is when there is a good chance the data will never be needed. Also, in an interactive application, it can be used to reduce startup time and give the illusionof speed.

在网络爬虫中对数据库使用延迟初始化可能不值得。延迟初始化增加了复杂性和持续的速度冲击。一种合理的情况是,很有可能永远不需要数据。此外,在交互式应用程序中,它可用于减少启动时间并给人以速度的错觉

For a non-interactive application like a web-crawler, which will surely need its database to exist right away, lazy initialization is a poor fit.

对于像网络爬虫这样的非交互式应用程序,它肯定需要立即存在其数据库,延迟初始化是不合适的。

On the other hand, a web-crawler is easily parallelizable, and will benefit greatly from being multi-threaded. Using it as an exercise to master the java.util.concurrentlibrary would be extremely worthwhile. Specifically, look at ConcurrentHashMapand ConcurrentSkipListMap,which will allow multiple threads to read and update a shared map.

另一方面,网络爬虫很容易并行化,并且将从多线程中受益匪浅。将其用作掌握java.util.concurrent库的练习将非常值得。具体来说,看看ConcurrentHashMapand ConcurrentSkipListMap这将允许多个线程读取和更新共享映射。

When you get rid of lazy initialization, the simplest Singleton pattern is something like this:

当你摆脱惰性初始化时,最简单的单例模式是这样的:

class Singleton {

  static final Singleton INSTANCE = new Singleton();

  private Singleton() { }

  ...

}

The keyword finalis the key here. Even if you provide a static"getter" for the singleton rather than allowing direct field access, making the singleton finalhelps to ensure correctness and allows more aggressive optimization by the JIT compiler.

关键字final是这里的关键。即使您为static单例提供了“getter”而不是允许直接字段访问,制作单例也final有助于确保正确性并允许 JIT 编译器进行更积极的优化。

回答by tvanfosson

The article you referenced only talks about making the creation of the singleton object, presumably a collection in this case, thread-safe. You also need a thread-safe collection so that the collection operations also work as expected. Make sure that the underlying collection in the singleton is synchronized, perhaps using a ConcurrentHashMap.

您引用的文章仅讨论了创建单例对象(在这种情况下可能是集合)线程安全。您还需要一个线程安全的集合,以便集合操作也能按预期工作。确保单例中的底层集合是同步的,可能使用ConcurrentHashMap

回答by Bob Cross

If you look at the very bottom of that article, you'll see the suggestion to just use a static field. That would be my inclination: you don't really need lazy instantiation (so you don't need getInstance()to be both an accessor and a factory method). You just want to ensure that you have one and only one of these things. If you really need global access to one such thing, I'd use that code sample towards the very bottom:

如果您查看该文章的最底部,您会看到只使用静态字段的建议。这将是我的倾向:您并不真正需要惰性实例化(因此您不需要getInstance()既是访问器又是工厂方法)。您只想确保您拥有这些东西中的一件,而且只有一件。如果您真的需要全局访问这样的事情,我会在最底部使用该代码示例

class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();

  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

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

Note that the Singleton is now constructed during the installation of static fields. This should work and not face the threading risks of potentially mis-synchronizing things.

请注意,单例现在是在安装静态字段期间构建的。这应该可以工作,并且不会面临潜在的错误同步事物的线程风险。

All that said, perhaps what you really need is one of the thread-safe data structures available in the modern JDKs. For example, I'm a big fan of the ConcurrentHashMap: thread safety plus I don't have to write the code (FTW!).

综上所述,也许您真正需要的是现代 JDK 中可用的线程安全数据结构之一。例如,我是ConcurrentHashMap 的忠实粉丝:线程安全加上我不必编写代码(FTW!)。

回答by Jeach

If your life depended on a few microseconds then I would advise you to optimize your resource locking to where it actually mattered.

如果您的生命取决于几微秒,那么我建议您将资源锁定优化到真正重要的地方。

But in this case the keyword here is hobby project!

但在这种情况下,这里的关键字是爱好项目

Which means that if you synchronized the entire getInstance()method you will be fine in 99.9% of all cases. I would NOT recommend doing it any other way.

这意味着,如果您同步整个getInstance()方法,则在 99.9% 的情况下都没有问题。我不建议以任何其他方式进行。

Later, if you prove by means of profiling that the getInstance()synchronization is the bottleneck of your project, then you can move on and optimize the concurrency. But I really doubt it will cause you trouble.

稍后,如果您通过分析证明getInstance()同步是您项目的瓶颈,那么您可以继续并优化并发。但我真的怀疑它会给你带来麻烦。

Jeach!

杰奇!

回答by Tawani

Check out this article Implementing the Singleton Pattern in C#

查看这篇文章在 C# 中实现单例模式

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

回答by mamboking

How about:

怎么样:

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

  return instance;
}

回答by Aries McRae

Try the Bill Pughsolution of initialization on demand holder idiom. The solution is the most portable across different Java compilers and virtual machines. The solution is thread-safe without requiring special language constructs (i.e. volatile and/or synchronized).

尝试使用Bill Pugh初始化按需持有者习语的解决方案。该解决方案在不同的 Java 编译器和虚拟机之间是最可移植的。该解决方案是线程安全的,不需要特殊的语言结构(即 volatile 和/或 synchronized)。

http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh

http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh

回答by Peter Lawrey

Why don't you create a data structure you pass to each of the threads as dependency injection. That way you don't need a singleton. You still need to make the thread safe.

为什么不创建一个数据结构作为依赖注入传递给每个线程。这样你就不需要单身人士了。您仍然需要使线程安全。

回答by Alexandros

as Joshua Bloch argues in his book "effective java 2nd edition" I also agree that a single element enum type is the best way to implement a singleton.

正如 Joshua Bloch 在他的书“ Effective java 2nd edition”中所说的那样,我也同意单元素枚举类型是实现单例的最佳方式。

public enum Singleton {
  INSTANCE;

  public void doSomething() { ... }
}