一开始,我们写的单例模式是这样的。
1 | public class Singleton { |
但是,这种方式有一个问题,就是无法解决多线程问题。
于是,对方法加锁:
1 | public class Singleton { |
这样,就解决了并发的问题。
但是,在每次调用方法我们都需要加锁,加锁实际上性能会变低,实际上调用方法只会出现一次instance==null,以后的每一次调用都是直接返回instance对象。
因此,可以将if判断语句提取出来。
1 | public class Singleton { |
但是,这样还是有问题,假设线程A执行完了lineA进入同步语句,但还并没有创建实例,此时线程B也执行到了lineA,但是instance==null,还是会重复创建实例。
于是,采用DCL解决,在同步语句块内部再进行一次if判断。
1 | public class Singleton { |
那么,这样就没有问题了吗?
并不是,因为new Singleton()这个语句实际上它并不是一个原子操作。
它有三条指令构成:
1.在堆中开辟一块内存(new)
2.调用对象的构造函数对内存进行初始化(invokespecial)
3.最后将引用赋值给变量(astore),这一句instance就赋值了。
所以,因为重排序的存在,CPU有可能产生指令重排序,比如1-3-2,这样的话,另一个线程可能在对象还没有初始化的时候就拿走了instance,造成问题。
于是,我们可以加上volatile,禁止指令重排序。
1 | public class Singleton { |