static Singleton instance = null;
static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
以上代码是典型的java双检锁构造单例模式的代码。看上去两次判空+类锁保证了多线程下的安全,其实不然,这种写法依然存在问题。
问题出现在 instance = new Singleton(); 这句话上。
这句话就是初始化一个对象,理论上这个操作的步骤大概是下面这个样子的:
- 分配一块内存M;
- 在内存M上初始化Singleton对象
- 将内存M的地址赋给instance。
实际上,jvm会对这个过程进行优化,优化过后的步骤是这样的: - 分配一块内存M;
- 将内存M的地址赋给instance;
- 在内存M上初始化Singleton对象。
那么问题来了,如果有两个线程A、B都在试图获取单例,A在执行到上述步骤2的时候,发生了线程切换,此时instance是被赋予了内存的地址的,所以不为空,这时B获得时间片,开始执行逻辑,走到第一句发现instance已经不为null,直接返回了这个引用。因为这个对象还没有初始化,后续通过这个引用方位内存上的对象时,会抛出NPE。
网友评论