两个Singleton实现同样多线程证明?

时间:2015-07-16 13:50:58

标签: java multithreading singleton

下面的代码片段中有两个Singleton实现。 它们是在Java中的多线程环境中实现Singletons的正确方法吗?

如果是这样,这是否意味着第二种实现方式完全没用,而只要你想实现一个Singleton,只需使用枚举方式?

我编写Singleton的优先方式:

public enum Singleton {
  INSTANCE();
}

编写Singleton的另一种方法(显式同步):

public class Singleton {

  private static Singleton instance = null;
  private Singleton() {}

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

4 个答案:

答案 0 :(得分:2)

你们两种方法都很好看。但是,为了创建Singleton,我将建议以下方法,它是100%线程安全且没有同步问题,因为它利用了Java的类加载机制。

P.S。:对@james的触发信用。

懒惰的单身人士:
像这个类一样,Provider只会加载一次,因此只存在一个Network类实例,并且没有两个线程可以在同一个JVM中创建2个实例,因为类不会被加载两次。

优点:

  • 只有在需要时才会创建实例。
  • 调用getInstance()时无需承担同步费用,这会在getInstance()同步时发生。
  • 类中可以有其他静态方法,可以在不创建实例的情况下调用,只在需要实例方法调用时才创建实例。这是懒惰单身人士的一个很好的优势。

    public class Network {
    private Network(){
    
    }
    
    private static class Provider {
        static final Network INSTANCE = new Network();
    }
    
    public static Network getInstance() {
        return Provider.INSTANCE;
    }
    //More code...
    

    }

渴望单身人士:

private static Network INSTANCE = null;

private Network(){
}

static {
    INSTANCE = new Network();
}

public static Network getInstance() {
    return INSTANCE;
}


最后:你说得对,你指定的第二种方法确实是最后一种选择,因为每次2个线程试图获取实例时,它必须承担同步的成本单身人士。

现在,它的ENUM单人v / s懒人单身游戏。我已经为你提供了很少的链接,说明为什么人们不喜欢单身人士的ENUM以及基于课程加载的ENUM单人v / s懒人单人的可扩展性。

这两种方法都可以无缝地工作,因此它的选择问题,因此真正的答案是"它取决于!!!" :)

答案 1 :(得分:2)

一些程序员讨论如何创建单例的原因是,他们希望支持延迟创建,但担心后续调用accessor方法的性能。

因此,使用synchronized的类实现了延迟创建方面,但后续调用可能会受到synchronized的不必要开销的影响。为了克服这个假设的开销,臭名昭着的双重检查锁定被发明,这引起了更多关于这个“问题”的讨论,这实际上完全无关紧要:

如果您想将懒惰创作与单例组合,您可以使用:

public class Singleton {
    static final Singleton INSTANCE=new Singleton();
    private Singleton(){}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

整个构造是惰性的,因为类初始化不会在第一个线程调用getInstance()之前发生并且它是线程安全的,因为类初始化本身保证是JVM的线程安全的。它是最有效的解决方案,因为后续调用只需读取final字段而无需同步。

这正是使用

时会发生的事情
public enum Singleton {
  INSTANCE;
}

它可以避免声明private构造函数,因为编译器会为您执行此操作。该字段隐含static final。一个区别是该字段本身是暴露的,不需要访问方法。

此外,enum支持序列化,本质上。存储常量时,不存储实例数据,只存储类型和常量名称。在反序列化时,将查找当前运行时实例。因此,如果您需要序列化支持,则应首选enum,否则没有太大区别(使用enum可以节省一些输入内容。)

但是

  • 您的应用程序中需要多长时间使用一个单例? (如果你说“经常”,你应该重新考虑你的软件设计)
  • 您的应用程序中单例的访问器方法的性能有多短?
  • 单例实例创建的懒惰多久一次?
  • 如果你需要一个单身人士,你多久需要一个关于单身人士财产的“铁难”保证?即,如果有人设法使用脏反射技巧创建第二个实例,那么可能出现的问题是他的问题,不是吗?

简单地说,单身人士设计模式似乎被高估了,并讨论了如何更有效地实施它......

答案 2 :(得分:0)

两者都是线程安全的,但是第一个使用枚举,避免了不必要的锁定,在大多数情况下更可取。

答案 3 :(得分:0)

第一个解决方案适用于创建singelton,因为您想要为某个事物建模。在此解决方案中,如果在创建未找到文件的实例期间发生异常,您将获得classnotfound异常。

第二种解决方案适用于创建singelton,因为对象的创建需要很长时间,并且涉及对文件系统或网络的访问。