Вопрос

Checkstyle сообщает об этом коде как «Идиома блокировки с двойной проверкой нарушена», но я не думаю, что на мой код действительно влияют проблемы с блокировкой с двойной проверкой.

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

Псевдокод:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

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

Я ошибаюсь или чекстайл?:)

Это было полезно?

Решение

Предполагая, что вы хотите, чтобы эта самая внутренняя строка читалась:

row = dao().create(id);

Это не классическая проблема блокировки с двойной проверкой, если допустить, что dao (). fetch правильно мьютексирован из метода create.

Изменить : (код был обновлен)

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

Если предположить, что DAO правильно синхронизирован и не вернет частично инициализированное значение, это не страдает недостатками идиомы двойной проверки блокировки.

Другие советы

Я думаю, что в этом случае контрольный стиль правильный. В представленном коде рассмотрите, что произойдет, если у двух потоков будет row == null при входе в синхронизированный блок. Поток A войдет в блок и вставит новую строку. Затем, после того как поток A выйдет из блока, поток B войдет в блок (потому что он не знает, что только что произошло) и попытается снова вставить ту же новую строку.

Я вижу, вы только что изменили код и добавили довольно важную отсутствующую строку. В коде new вы можете обойтись без этого, поскольку два потока не будут полагаться на изменения общей (статической) переменной. Но вам может быть лучше увидеть, поддерживает ли ваша СУБД такой оператор, как INSERT OR UPDATE .

Еще одна веская причина для делегирования этих функций СУБД - это необходимость развертывания более одного сервера приложений. Поскольку блоки synchronized не работают на разных машинах, вам все равно придется что-то делать в этом случае.

Если у вас возникнет желание написать такой код, подумайте:

  • Начиная с Java 1.4, синхронизация методов стала довольно дешевой.Это не бесплатно, но среда выполнения действительно не так сильно страдает, чтобы стоит рисковать повреждением данных.

  • Начиная с Java 1.5, у вас есть классы Atomic*, которые позволяют вам читать и устанавливать поля атомарным способом.К сожалению, они не решают вашу проблему.Почему они не добавили AtomicCachedReference или что-то в этом роде (что вызывало бы переопределяемый метод при вызове get() и текущем значении == null) мне непонятно.

  • Пытаться ehcache.Он позволяет вам настроить кеш (т.и объект, который позволяет вам вызывать код, если ключ нет содержится на карте).Обычно это то, что вам нужно, и кеши действительно решают вашу проблему (и все другие проблемы, о существовании которых вы даже не подозревали).

Как уже отмечали другие, этот код будет делать то, что вы намерены, как есть, но только при строгом наборе неочевидных предположений:

<Ол>
  • Java-код не сгруппирован (см. ответ @Greg H)
  • " строка " ссылка only проверяется на нулевое значение в первой строке перед блоком синхронизации.
  • Причина, по которой дважды проверяется идиома блокировки (согласно разделу 16.2.4 Java Concurrency на практике ) то, что поток, выполняющий этот метод, может видеть ненулевую, но неправильно инициализированную ссылку на " строку " ;, перед входом в синхронизированный блок (если " dao " не обеспечивает надлежащей синхронизации) , Если бы ваш метод что-то делал с " строкой " кроме проверки, что это нуль или нет, он будет сломан. В нынешнем виде, вероятно, все в порядке, но очень хрупкий - лично мне было бы неудобно фиксировать этот код, если бы я думал, что есть даже отдаленный шанс, что какой-то другой разработчик в какое-то время может изменить метод без понимания тонкостей DCL.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top