Почему Netbeans жалуется на то, что синхронизированный вызов обернут условным кодом? - PullRequest
2 голосов
/ 09 декабря 2011

Скажите, у меня есть такой код:

private static Thing t = null;
private static final Object lock = new Object();   

public static void foo() {
    if(t == null) { //Netbeans warning on this line.
        synchronized(lock) {
            if(t == null) {
                t = new Thing();
            }
        }
    }
    ...Do stuff with t...
}

Теперь, foo размещен на сервере и может быть вызван многими пользователями одновременно. Цель функции foo состоит в том, чтобы инициализировать t, если это еще не было, в противном случае использовать инициализированный экземпляр, чтобы делать все, что нужно.

Однако Netbeans выдает мне предупреждение о таком коде:

Remove the outer conditional statement

Подсказка:

Double-checked locking

Смысл двойной проверенной блокировки состоит в том, чтобы избежать узкого места, ожидающего, чтобы люди сняли блокировку. Если бы единственная проверка, чтобы видеть, было ли t нулем, была внутри синхронизированного блока, все должны были бы ждать своего шанса, чтобы видеть, был ли t нулем, и все-1 фактически ничего не ждали бы (потому что t не был бы нулем) , Отсюда и внешнее условное.

С его помощью, скажем, каким-то чудом, 5 человек одновременно принимают t == null в true. Один пользователь получит синхронизированную блокировку, увидит, что t == null все еще имеет значение true, и инициализирует ее. Затем этот пользователь снимает блокировку, и следующий пользователь блокирует ее и почти сразу же снимает ее (как t! = Null больше), это будет продолжаться до тех пор, пока 5 пользователей не пройдут через нее. Все это время другие подключающиеся пользователи перепрыгивают через синхронизированную блокировку, поскольку первый пользователь сделал t! = Null.

Итак, почему Netbeans жалуется? Похоже, это лучший способ сделать это AFAIK.

Мысли

1 Ответ

5 голосов
/ 09 декабря 2011

Это жалобы, потому что, как написано, это небезопасно - или, по крайней мере, может быть.

Поскольку t не volatile, поток чтения может иметь возможность прочитать новое значение t , пока Thing все еще создается . Это был бы плохой ход - использование частично построенных объектов отвратительно.

До новой модели памяти Java 5 она даже не была бы поточно-ориентированной с полем, помеченным как volatile. С новой моделью памяти, которая безопасна в определенных ситуациях, возможно, даже без volatile, но лично я бы все равно избежал:

  • Простая синхронизация в каждом вызове вряд ли будет существенным узким местом в большинстве приложений
  • Обычно инициализация как часть статического инициализатора достаточно хороша:

    private static Thing t = new Thing();
    
  • Для других случаев существуют альтернативные приемы с вложенными классами, которые все еще используют лень статических инициализаторов, но все это единственное свойство быть ленивым, даже когда к другим элементам обращаются.

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

Для получения лота дополнительной информации о старой неисправности DCL читайте Декларация «Двойная проверка блокируется» 10 * *. (См. Последний раздел для текущей ситуации с JDK 5 - но подумайте, действительно ли вы хотите, чтобы сопровождающие вашего кода думали обо всем этом.)

...