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内存模型对类初始化的保证来确保线程安全。 每个类只能加载一次,只有在需要时才加载。 这意味着,第一次调用getInstance
, InstanceHolder
将被加载, 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,则不需要
- 静态的投资
- 枚举
- 具有Initialization-on-demand_holder_idiom的LazyInitalaization
详情请参阅此问题
Java Singletondevise模式:问题
public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis () {...} }
来源:有效的Java – >项目2
它build议使用它,如果你确定这个类将永远保持单身。