Еще один вопрос по поводу «Двойной проверки-блокировки» - PullRequest
0 голосов
/ 08 марта 2019

Я изменил "нормальный" случай синглтона DCL в соответствии с "действующей java" # 83, как показано ниже.

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();
    }
}

Ответы [ 3 ]

1 голос
/ 08 марта 2019

Вы получаете NPE, когда происходит следующее:

public static MySystem getInstance() {
    MySystem my = instance;
    if (my == null) {                            // (1) instance was null => my is null and synchronized block is entered.
        synchronized (MySystem.class) {
            if (instance == null) {              // (2) instance was updated from another thread and is not null anymore.
                instance = my = new MySystem();
            }
        }
    }
    return my;
}

Вы заметите, что в этом сценарии ссылка instance не копируется в my, которая остается null. Вы можете попробовать следующее для проверки:

public static MySystem getInstance() {
    MySystem my = instance;
    if (my == null) {
        synchronized (MySystem.class) {
            if (instance == null) {
                instance = new MySystem();
            }
        }
        my = instance;
    }
    return my;
}
1 голос
/ 08 марта 2019

Разница в этой строке:

my = instance;

Вы делаете ссылки на оба объекта одним местом в куче JVM. После этого вы звоните:

my = new MySystem();

, что делает и my, и instance не нулевыми (вы не можете связать оператор =, поэтому создается только my). Затем после вызова этого:

MySystem.getInstance().getDate()

вы не вызываете метод для null.

Когда он синхронизирован, второй поток ожидает создания экземпляра my (для вызова этой строки my = instance) и не получает NPE.

0 голосов
/ 08 марта 2019

Хорошо, позвольте мне объяснить это шаг за шагом. Поток A: мой == ноль. Поток B: my == null, затем получить синхронизацию, затем "instance = my = new MySystem ()" и вернуть my, который не является нулевым. Поток A: получить синхронизацию, затем "instance! = Null" и вернуть my, что является нулем.

NPE, взрыв! Так что "my = instance" перед второй проверкой необходимо.

Как объяснить пример в "Эффективной Java"?

// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
private FieldType getField() {
    FieldType result = field;
    if (result == null) { // First check (no locking)
        synchronized(this) {
        if (field == null) // Second check (with locking)
            field = result = computeFieldValue();
        }
    }
    return result;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...