Java单例和同步

请澄清我关于单例和multithreading的查询:

  • 在multithreading环境下,用Java实现Singleton的最好方法是什么?
  • 当多个线程同时尝试访问getInstance()方法时会发生什么情况?
  • 我们可以让singleton的getInstance() synchronized吗?
  • 真的需要同步,当使用Singleton类?

是的,这是必要的。 有几种方法可以用来通过延迟初始化来实现线程安全:

恶魔同步:

 private static YourObject instance; public static synchronized YourObject getInstance() { if (instance == null) { instance = new YourObject(); } return instance; } 

这个解决scheme要求每个线程在实际上只有前几个需要时才能被同步。

仔细检查同步 :

 private static final Object lock = new Object(); private static volatile YourObject instance; public static YourObject getInstance() { YourObject r = instance; if (r == null) { synchronized (lock) { // While we were waiting for the lock, another r = instance; // thread may have instantiated the object. if (r == null) { r = new YourObject(); instance = r; } } } return r; } 

这个解决scheme确保只有前几个尝试获取你的单例的线程必须经过获取锁的过程。

按需初始化 :

 private static class InstanceHolder { private static final YourObject instance = new YourObject(); } public static YourObject getInstance() { return InstanceHolder.instance; } 

该解决scheme利用Java内存模型对类初始化的保证来确保线程安全。 每个类只能加载一次,只有在需要时才加载。 这意味着,第一次调用getInstanceInstanceHolder将被加载, instance将被创build,并且由ClassLoader控制,所以不需要额外的同步。

这种模式在没有显式同步的情况进行线程安全的实例延迟初始化!

 public class MySingleton { private static class Loader { static final MySingleton INSTANCE = new MySingleton(); } private MySingleton () {} public static MySingleton getInstance() { return Loader.INSTANCE; } } 

它的工作原理是它使用类加载器为您免费进行所有同步: MySingleton.Loader类首先在getInstance()方法内部访问,所以Loader类在第一次调用getInstance()时加载。 此外,类加载器保证所有的静态初始化在你访问类之前完成 – 这就是你的线程安全。

这就像魔术一样。

它实际上与Jhurtado的枚举模式非常相似,但我发现枚举模式滥用枚举概念(虽然它工作)

如果您正在使用Java的multithreading环境,并且需要保证所有这些线程正在访问一个类的单个实例,则可以使用Enum。 这将有助于您处理序列化的附加优势。

 public enum Singleton { SINGLE; public void myMethod(){ } } 

然后让你的线程使用你的实例:

 Singleton.SINGLE.myMethod(); 

是的,你需要使getInstance()同步。 如果不是,那么可能会出现这样的情况,即可以制作多个class级的实例。

考虑一下你有两个线程同时调用getInstance() 。 现在想象一下T1执行刚刚过去的instance == null检查,然后T2运行。 此时实例未被创build或设置,因此T2将通过检查并创build实例。 现在想象执行切换回T1。 现在单身人士被创build,但T1已经做了检查! 它会继续做对象! 使getInstance()同步可以防止这个问题。

有几种方法可以让单例是线程安全的,但是让getInstance()同步可能是最简单的。

枚举单例

实现一个线程安全的单例最简单的方法是使用Enum

 public enum SingletonEnum { INSTANCE; public void doSomething(){ System.out.println("This is a singleton"); } } 

自从Java 1.5引入了Enum以来,这个代码就起作用了

双重检查locking

如果你想编写一个在multithreading环境下工作的“经典”单例(从Java 1.5开始),你应该使用这个。

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

这在1.5之前不是线程安全的,因为volatile关键字的实现是不同的。

早期加载Singleton(甚至在Java 1.5之前工作)

这个实现在类加载时实例化单例,并提供线程安全性。

 public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public void doSomething(){ System.out.println("This is a singleton"); } } 

您还可以使用静态代码块在类加载时实例化实例,并防止线程同步问题。

 public class MySingleton { private static final MySingleton instance; static { instance = new MySingleton(); } private MySingleton() { } public static MySingleton getInstance() { return instance; } } 

在multithreading环境下,用Java实现Singleton的最好方法是什么?

参考这篇文章,了解实现Singleton的最佳方法。

什么是在Java中实现单例模式的有效方法?

当多个线程同时尝试访问getInstance()方法时会发生什么情况?

这取决于你实现方法的方式。如果你使用不带volatilevariables的双锁,你可能会得到部分构造的Singleton对象。

有关更多详细信息,请参阅此问题:

为什么在这个双重检查locking的例子中使用了volatile

我们可以让singleton的getInstance()同步吗?

真的需要同步,当使用Singleton类?

如果以下面的方式实现Singleton,则不需要

  1. 静态的投资
  2. 枚举
  3. 具有Initialization-on-demand_holder_idiom的LazyInitalaization

详情请参阅此问题

Java Singletondevise模式:问题

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

来源:有效的Java – >项目2

它build议使用它,如果你确定这个类将永远保持单身。