基础版
- 这个实现是有线程安全问题的;
- 当线程1停在 lazySingleton = new LazySingleton(); 这行时,线程2还是可以趁线程1停着的时候进入if块实例化 lazySingleton 的,结果就是new出来2个LazySingleton实例;
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton(){}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
线程安全问题重现
- 在2个线程中分别调用LazySingleton的getInstance(),使用多线程Debug的方式,将Thread-0停在 lazySingleton = new LazySingleton(); 这句,此时让Thread-1顺利执行,Thread-1将new一个LazySingleton实例出来;再让Thread-0继续执行,Thread-0又将new一个LazySingleton实例,从而演示出在多线程中创建出2个LazySingleton实例的情况;
public class T implements Runnable {
@Override
public void run() {
LazySingleton lazySingleton = LazySingleton.getInstance();
System.out.println(Thread.currentThread().getName() + " : " + lazySingleton);
}
}
public class Test {
public static void main(String[] args) {
Thread t1 = new Thread(new T());
Thread t2 = new Thread(new T());
t1.start();
t2.start();
System.out.println("Program End");
}
}
输出:
Thread-0 : designpattern.creational.singleton.LazySingleton@2a8979e
Thread-1 : designpattern.creational.singleton.LazySingleton@2b79559a
Program End
解决方案
- 在getInstance()方法上加锁;
- 由于getInstance()是静态方法,在静态方法上加锁的范围其实是在整个类上加锁,开销较大,如果方法不是静态方法,那么锁的范围是在堆内存上;
- 更好的方案是Double Check;
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton(){}
public synchronized static LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}