Подтвердить что ты не робот

Двойная проверка блокировки в синглтоне

вот мой пользовательский класс для singleton pattern. в этом коде я использую блокировку с двойной проверкой, как показано ниже. Когда я читаю много сообщений в каком-то источнике, они говорят, что двойная проверка полезна, потому что она предотвращает одновременное выполнение двух одновременных потоков двумя разными объектами.

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private attribute of this class
        private String searchWord = "";
        private String[] list = new String[]{"Stack", "Overflow"};

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

Я до сих пор не понимаю выше кода. В чем проблема, если два потока вместе работают с одной строкой кода, когда экземпляр имеет значение null?

if (searchBox == null) {
                synchronized (SearchBox.class) {
                    if (searchBox == null) {
                        searchBox = new SearchBox();
                    }
                }
            }

Когда это появится. оба двух потока будут видеть, что объект имеет значение null. то обе синхронизируются. а затем, , они снова проверяют и все еще видят его null. и создать два разных объекта. OOOPS.

Пожалуйста, объясните мне. Что я понимаю неправильно?

Спасибо:)

4b9b3361

Ответ 1

Нет, поскольку вы получаете блокировку на SearchBox.class, только один поток будет вводить синхронизированный блок за раз. Итак, первый поток входит, затем находит searchBox и создает его, а затем покидает синхронизированный блок, затем второй поток входит в блок, после чего обнаруживает, что searchBox не является нулевым, потому что первый поток уже создал его, чтобы он не создавал новый экземпляр searchBox

Двойной проверенный шаблон используется, чтобы избежать получения блокировки каждый раз, когда выполняется код, если вызов не происходит вместе, тогда первое условие завершится неудачно, и выполнение кода не будет выполнять блокировку, тем самым экономя ресурсы.

Ответ 2

Посмотрите на этот код:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

Попробуй рассуждать об этом. Пусть говорят, что у нас есть две нити A и B, и предположим, что хотя бы одна из них достигает линии 3 и наблюдает searchBox == null is true. Два потока не могут одновременно быть на линии 3 одновременно из-за блока synchronized. Это ключ к пониманию того, почему работает двойная проверка блокировки. Таким образом, это должно быть так, что либо A, либо B выполнили сначала synchronized. Не теряя общности, скажем, что этот поток A. Затем, увидев, что searchBox == null истинно, он войдет в тело оператора и установит searchBox в новый экземпляр searchBox. Затем он выйдет из блока synchronized. Теперь это будет B, чтобы войти: помните, B был заблокирован, ожидая выхода A. Теперь, когда он войдет в блок, он будет наблюдать searchBox. Но A остался бы только установив searchBox в значение не null. Готово.

Кстати, в Java лучший способ реализовать одноэлемент - использовать одноэлементный тип enum. Из Эффективная Java:

Хотя этот подход еще не принят, одноэлементный тип перечисления - лучший способ реализовать одноэлемент.

Ответ 3

Эта блокировка двойной блокировки необходима только в том случае, если вы беспокоитесь о многих потоках, вызывающих однотонный сигнал одновременно, или о стоимости получения блокировки в целом.

Его цель - предотвратить ненужную синхронизацию, тем самым быстро сохраняя ваш код в многопоточной среде.

Ознакомьтесь с этой ссылкой для получения дополнительной информации.

Если вы работаете в Java 1.5 или выше, и используете ключевое слово volatile в вашем заблокированном механизме двойной проверки, он будет работать нормально. Когда вы используете ключевое слово volatile, ваш пример не разбит по той же ссылке выше.

Ответ 4

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  • Если экземпляр уже создан, ничего не делайте - избегайте блокировки потоков
  • Первый поток, который приобрел блокировку, проверяет и видит, что такого объекта нет и не создает. Он освобождает блокировку, а второй может делать то же самое - он должен проверить, существует ли объект, потому что первый из них мог его создать.

Таким образом, внешний if используется для предотвращения избыточных блокировок - он позволяет всем потокам знать, что уже есть объект, и им не нужно блокировать/делать что-либо. И внутренний if используется, чтобы позволить параллельному потоку знать, был ли другой уже создан объект или нет.