10

所以我正在考虑建立一个业余爱好项目,一种特殊的东西,只是为了复习我的编程/设计。

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

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

我应该研究另一种方法吗?

4

10 回答 10

30

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

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

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

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

或静态初始化它

private static final Singleton INSTANCE = new Singleton();
于 2009-04-22T18:42:52.357 回答
10

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

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

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

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

class Singleton {

  static final Singleton INSTANCE = new Singleton();

  private Singleton() { }

  ...

}

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

于 2009-04-22T15:39:08.257 回答
2

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

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

于 2009-04-23T07:20:20.523 回答
2

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

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

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

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

杰奇!

于 2009-04-22T15:44:57.933 回答
2

正如 Joshua Bloch 在他的《有效的 java 2nd edition》一书中所说,我也同意单元素枚举类型是实现单例的最佳方式。

public enum Singleton {
  INSTANCE;

  public void doSomething() { ... }
}
于 2010-07-07T14:29:31.320 回答
1

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

于 2009-04-23T18:11:04.817 回答
1

如果您查看该文章的最底部,您会看到仅使用静态字段的建议。那将是我的倾向:您实际上并不需要惰性实例化(因此您不需要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;
  }
}

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

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

于 2009-04-22T15:44:13.567 回答
0

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

于 2009-04-22T15:41:26.007 回答
0

查看这篇文章在 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();
    }
}
于 2009-04-22T18:45:59.463 回答
0

怎么样:

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

  return instance;
}
于 2009-04-22T18:47:36.480 回答