代码之家  ›  专栏  ›  技术社区  ›  Dan.StackOverflow

是否正确使用同步单件?

  •  10
  • Dan.StackOverflow  · 技术社区  · 15 年前

    所以我想做一个业余爱好项目,一件事,只是为了复习一下我的编程/设计。

    它基本上是一个多线程web spider,更新相同的数据结构对象--gt;int。

    因此,使用一个数据库来实现这一点绝对是多余的,我唯一能想到的就是一个线程安全的singleton,它用来包含我的数据结构。 http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html

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

    10 回复  |  直到 10 年前
        1
  •  29
  •   Armel Larcier    10 年前

    双重检查锁定已被证明是不正确的和有缺陷的(至少在爪哇)。搜索或查看 Wikipedia's entry 因为确切的原因。

    首先,也是最重要的是程序正确性。如果您的代码不是线程安全的(在多线程环境中),那么它将被破坏。正确性先于性能优化。

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

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

    或者静态初始化它

    private static final Singleton INSTANCE = new Singleton();
    
        2
  •  10
  •   erickson    15 年前

    在Web爬虫程序中对数据库使用惰性初始化可能是不值得的。懒惰的初始化增加了复杂性和持续的速度冲击。有一种情况是合理的,那就是当很有可能永远不需要数据时。此外,在交互式应用程序中,它可以用来缩短启动时间,并给出 幻觉 速度。

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

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

    当您摆脱懒惰的初始化时,最简单的单例模式如下所示:

    class Singleton {
    
      static final Singleton INSTANCE = new Singleton();
    
      private Singleton() { }
    
      ...
    
    }
    

    关键词 final 钥匙在这里吗?即使你提供了 static “getter”用于singleton,而不是允许直接字段访问,使singleton 最终的 有助于确保正确性,并允许JIT编译器进行更积极的优化。

        3
  •  2
  •   Jeach    15 年前

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

    但在这种情况下,这里的关键字是 嗜好计划 !

    也就是说如果你把整个 获取实例() 方法所有病例中99.9%都可以。我不建议用其他方法。

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

    各位!

        4
  •  2
  •   Aries McRae    15 年前

    试试 比尔-普格 初始化随需应变持有者习语的解决方案。 该解决方案在不同的Java编译器和虚拟机上是最便携的。 该解决方案是线程安全的,不需要特殊的语言构造(即volatile和/或synchronized)。

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

        5
  •  2
  •   Alexandros    14 年前

    正如Joshua Bloch在他的书《有效Java第二版》中所说的,我也同意单元素枚举类型是实现单体的最好方法。

    public enum Singleton {
      INSTANCE;
    
      public void doSomething() { ... }
    }
    
        6
  •  1
  •   Bob Cross n8wrl    15 年前

    如果您查看文章的最底层,您将看到只使用静态字段的建议。这就是我的倾向:你不需要懒惰的实例化(所以你不需要 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;
      }
    }
    

    注意,现在在安装静态字段的过程中构造了单例。这应该可以工作,而不是面临线程化风险,因为可能会发生误同步。

    尽管如此,也许您真正需要的是现代JDK中可用的线程安全数据结构之一。例如,我是 ConcurrentHashMap :线程安全加上我不必编写代码(ftw!).

        7
  •  1
  •   Peter Lawrey    15 年前

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

        8
  •  0
  •   tvanfosson    15 年前

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

        9
  •  0
  •   Tawani    15 年前

    看看这篇文章 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();
        }
    }
    
        10
  •  0
  •   mamboking    15 年前

    怎么样:

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