正确使用同步单身?

时间:2009-04-22 15:33:05

标签: java multithreading singleton synchronized

因此,我正在考虑建立一个爱好项目,一种类似的东西,只是为了了解我的编程/设计。

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

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

我应该考虑采用不同的方法吗?

10 个答案:

答案 0 :(得分:30)

双重检查锁定已被证明是错误的和有缺陷的(至少在Java中)。进行搜索或查看Wikipedia's entry的确切原因。

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

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

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

或静态初始化

private static final Singleton INSTANCE = new Singleton();

答案 1 :(得分:10)

对Web爬网程序中的数据库使用延迟初始化可能不值得。延迟初始化增加了复杂性和持续的速度命中。一个合理的案例是,很有可能永远不需要数据。此外,在交互式应用程序中,它可用于减少启动时间并提供错觉的速度。

对于像web-crawler这样的非交互式应用程序,它肯定需要立即存在其数据库,懒惰初始化是不合适的。

另一方面,Web爬虫很容易并行化,并且可以从多线程中受益匪浅。使用它作为掌握java.util.concurrent库的练习将是非常值得的。具体来说,请查看ConcurrentHashMapConcurrentSkipListMap,,这将允许多个线程读取和更新共享地图。

当您摆脱延迟初始化时,最简单的Singleton模式是这样的:

class Singleton {

  static final Singleton INSTANCE = new Singleton();

  private Singleton() { }

  ...

}

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

答案 2 :(得分:2)

如果您的生活依赖于几微秒,那么我建议您优化资源锁定到实际重要的位置。

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

这意味着,如果您同步整个 getInstance()方法,那么在99.9%的情况下你都可以。我不建议任何其他方式。

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

Jeach!

答案 3 :(得分:2)

尝试 Bill Pugh 解决方案初始化按需持有人习惯用法。 该解决方案在不同的Java编译器和虚拟机中最易于移植。 该解决方案是线程安全的,无需特殊的语言结构(即易失性和/或同步)。

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

答案 4 :(得分:2)

正如Joshua Bloch在他的书“有效的Java第二版”中所论证的那样,我也同意单个元素枚举类型是实现单例的最佳方式。

public enum Singleton {
  INSTANCE;

  public void doSomething() { ... }
}

答案 5 :(得分:1)

如果您查看该文章的最底部,您将看到仅使用静态字段的建议。这将是我的倾向:你并不需要懒惰的实例化(因此你不需要getInstance()同时成为访问者和工厂方法)。您只想确保您拥有其中一项且仅有一项。如果你真的需要全局访问这样的东西,我会使用that code sample towards the very bottom

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

请注意,现在在安装静态字段期间构建了Singleton。这应该有效,而不是面临潜在的错误同步的线程风险。

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

您引用的文章仅讨论如何创建单例对象,在本例中可能是一个集合,是线程安全的。您还需要一个线程安全的集合,以便集合操作也按预期工作。确保单例中的基础集合是同步的,可能使用ConcurrentHashMap

答案 8 :(得分:0)

查看此文章Implementing the Singleton Pattern in 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();
    }
}

答案 9 :(得分:0)

怎么样:

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

  return instance;
}