首页 > 解决方案 > 关于“双重检查锁定”的另一个问题

问题描述

我已经根据“有效的 java”#83 修改了一个“正常”的 DCL 单例案例,如下所示。

import java.util.Date;

public class MySystem {
    private Date date = new Date();

    private MySystem() {};

    private static volatile MySystem instance;

    public Date getDate() {
        return date;
    }

    public static MySystem getInstance() {
        MySystem my = instance;
        if (my == null) {
            synchronized (MySystem.class) {
                if (instance == null) {
                    instance = my = new MySystem();
                }
            }
        }
        return my;
    }
}

但是当我运行它时,NullpointerException 会以非常高的比例被抛出。当我如下修改它时,一切正常。为什么?

import java.util.Date;

public class MySystem {
    private Date date = new Date();

    private MySystem() {};

    private static volatile MySystem instance;

    public Date getDate() {
        return date;
    }

    public static MySystem getInstance() {
        MySystem my = instance;
        if (my == null) {
            synchronized (MySystem.class) {
                my = instance;
                if (my == null) {
                    instance = my = new MySystem();
                }
            }
        }
        return my;
    }
}

主要如下。很难弄清楚其中的区别。

public class Main {
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                System.out.println(MySystem.getInstance().getDate());
            }
        }.start();

        new Thread() {
            public void run() {
                System.out.println(MySystem.getInstance().getDate());
            }
        }.start();
    }
}

标签: javamultithreadingconcurrencyjvmsingleton

解决方案


不同之处在于这一行:

my = instance;

您正在将两个对象引用都放在 JVM 堆上的一个位置。之后你打电话:

my = new MySystem();

这使得两者my都不是instance空的(你不能链接=运算符,所以只有my实例化)。然后在调用这个之后:

MySystem.getInstance().getDate()

您没有在 null 上调用方法。

当它同步时,第二个线程正在等待my(为了调用此行my = instance)的实例化并且没有获得 NPE。


推荐阅读