与饿汉式相反的地方在于当被需要时才实例化一个对象,所以导致一个问题是当有多个线程同时访问 getInstance() 时,可能会创建多个实例对象,即是线程不安全的。<br>当然好处是避免了饿汉式存在的浪费系统内存资源的问题。
代码改进
public class LazySimpleSingleton {<br> private LazySimpleSingleton() {<br> }<br> private static LazySimpleSingleton lazySimpleSingleton = null;<br> public static LazySimpleSingleton getInstance() {<br> if (lazySimpleSingleton == null) {<br> lazySimpleSingleton = new LazySimpleSingleton();<br> }<br> return lazySimpleSingleton;<br> }<br>}
public class LazySynchronizedSingleton {<br> private LazySynchronizedSingleton() {<br> }<br> private static LazySynchronizedSingleton lazySynchronizedSingleton = null;<br> public static synchronized LazySynchronizedSingleton getInstance() {<br><br> if (lazySynchronizedSingleton == null) {<br> return new LazySynchronizedSingleton();<br> }<br> return lazySynchronizedSingleton;<br> }<br>}
public class LazyDCLSingleton {<br> private LazyDCLSingleton() {<br> //如果通过反射访问到构造方法,也要判断一下,如果实例存在就抛异常<br> if (lazyDCLSingleton != null) {<br> throw new IllegalStateException("Already initialized.");<br> }<br> }<br> private static volatile LazyDCLSingleton lazyDCLSingleton = null;<br> //volatile 保证 lazyDCLSingleton 的可见性,以及防止关于 lazyDCLSingleton 操作的地方的指令重排<br> //当实例是空的,这时会有多个线程同时判断到 result 是空的,<br> //然后第一个线程拿到锁之后(其他的线程等待锁),判断 result 是 null,这个时候创建出我们的单例对象<br> //而由于 JVM 的优化指令重排,这三步可能并不会按顺序执行(因为 2、3 两个步骤是不相关的),可能按照 1、3、2 的顺序来执行<br> //这个时候就出问题了,当第一个线程来到执行了 1、3 的时候,这个时候 instance 指向内存了,非空,但是并没有被实例化<br> //恰巧第二个线程来到了第一个非空判断(锁外面的非空判断),发现 instance 不是空的,高高兴兴的 return 回去用了。<br> public static LazyDCLSingleton getInstance() {<br> if (lazyDCLSingleton == null) {<br> //如果实例是空的,就加锁<br> synchronized (LazyDCLSingleton.class) {<br> //如果实例还是空的,就new一个,然后返回;<br> if (lazyDCLSingleton == null) {<br> lazyDCLSingleton = new LazyDCLSingleton();<br> //1.分配内存给这个对象<br> //2.初始化对象-调用构造器来创建对象<br> //3.设置lazy指向刚分配得内存地址<br> //4.初次访问对象<br> }<br> }<br> }<br> return lazyDCLSingleton;<br> }<br>}<br>