Java 如何使单例类线程安全?

Java 如何使单例类线程安全?,java,class,singleton,class-design,Java,Class,Singleton,Class Design,我正在Java中实现一个单例类,以确保创建的类实例不超过一个。单例不是默认的线程安全的。创建单例的常用方法是使用一个工厂方法来处理一个实例的创建。这样您就可以控制实例的创建 public class Singleton { public static final Singleton INSTANCE = new Singleton(); private Singleton() { ... } } 使用静态实例变量比使用内部“Holder”类更可取。如果愿意,还可以将静态成员设为私有,并

我正在Java中实现一个单例类,以确保创建的类实例不超过一个。

单例不是默认的线程安全的。创建单例的常用方法是使用一个工厂方法来处理一个实例的创建。这样您就可以控制实例的创建

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

使用静态实例变量比使用内部“Holder”类更可取。如果愿意,还可以将静态成员设为私有,并提供访问它的方法,但所做的只是向堆栈中添加另一个方法调用。

惰性实例如何:getInstance()返回单例,或者如果它是第一个调用,则创建它

 public class MySingleton
 {
     private static MySingleton instance;

     private MySingleton()
     {
         // construct object . . .
     }

     // For lazy initialization
     public static synchronized MySingleton getInstance()
     {
         if (instance==null)
         {
             instance = new MySingleton();
         }
         return instance;
     }

     // Remainder of class definition . . .
 } 

单身的最佳方式是什么?使用枚举

public enum Singleton {
    INSTANCE;
    public void foo(){ ... }
}

// Usage:
Singleton.INSTANCE.foo();

VM不仅可以帮助您避免双重实例化,还可以帮助您避免反序列化损坏。

如果您关心并发性,您将不会使用共享全局状态。

也许最好的方法是对单个实例使用枚举。这还有一个额外的好处,就是可以序列化,并保证单例不受序列化和反射的影响,这是任何“简单”的单例实现都不能做到的(私有?我有反射,我嘲笑你的访问修饰符!)。实现起来也非常简单:

public enum Singleton {
    INSTANCE;

    // fields and methods go here
}

这取决于你想要实现什么,以及你更喜欢什么——持有者给你一个懒惰的单例,简单的版本不会给你懒惰的加载。是的,它会。Java在第一次使用类之前不会加载它们。这不完全正确。它在它们第一次被引用时加载它们,例如在另一个类的方法签名中。访问器方法通常由VM内联。不要选择公共字段而不是访问器来摆脱“堆栈上的方法调用”-在代码中,它看起来像是方法调用,但实际上是一样的。延迟实例化几乎从来没有必要,如果是,Java会延迟加载类,因此,在声明中创建实例实际上是延迟实例化——没有同步开销(在极少数情况下,这不够延迟,skaffman的答案更好)。还要注意,此实现会同步每次尝试获取实例引用的行为。因为大多数代码都会执行以下操作:Singleton.getInstance.doSomething();Singleton.getInstance.doSomethingElse();总体效果是额外的同步严重限制了并发性,在成员访问/变异方面没有线程安全保证。简而言之,java中的双重检查锁定被打破了,这种保证该类只实例化一次的方式可能会导致难以发现的并发性问题。永远不要使用这个。从Java 5开始,DCL是未损坏的。查看,向下滚动到“在新Java内存模型下”。如果单例保存了单个并行化问题的累积结果,为什么不呢?单例是全局状态的缩影,因此它将是一个公平使用的场景。对于仅用一个实例表示的类,有几个用例在进程之间共享。使用spring的典型设计良好的webapp可能会在单例范围内实例化一组类,在工作线程之间共享。