通常我们设计一个单例模式时,会在内部构造一个类,并对外提供一个static getInstance方法提供回去该单利对象的方法【ps这种方式我们也成为惰性加载】
public class Singleton { private static Singleton uniqueInstance = null; private Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // Other methods... }
但是这中方式在多线程中问题:
如果两个线程A和B同时执行了该方法,执行如下:
1: A线程进入if判断,此时fool为null,则进入if内部
2: B线程进入if判断,此时A还没有创建fool,因此fool为null,所以B也进入if内部
3: A创建了fool返回
4:B创建了fool返回
此时我们发现,我们的单例被创建了两次,这不是我们需要的结果
所以我们可以使用class的锁机制给getInstance方法加一个synchronize前缀,这样我们可以做到每次在同一时刻只允许一个线程调用getInstance
public class Singleton { private static Singleton uniqueInstance = null; private Singleton() { // Exists only to defeat instantiation. } //加入synchronized锁 public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // Other methods... }
这种解决方法可以防止错误的出现,但是最大的为题是影响性能:每次调用getInstance的时候必须获取synchronized锁,而实际上,当单例模式被创建后,其后的请求没必要再使用互斥机制了。。。
曾今有人使用过double-checked locking方法来解决这个为题【大家到网上搜索哈】
为了实现惰性加载,并且不希望每次调用getInstance都必须互斥,解决方案如下:
public class Singleton { private Singleton() { // Exists only to defeat instantiation. } private static class SingletoContainer(){ private static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletoContainer.instance; } // Other methods... }
JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样,当我们第一次调用getInstance的时候,JVM能够帮助我们instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,该方法只会在第一次调用的时候互斥