Безопасно ли использовать для синхронизации локальную блокировку вместо общей блокировки? - PullRequest
2 голосов
/ 11 августа 2010

Я попытаюсь объяснить вопрос, взяв следующие 3 случая. ДЕЛО I: Я использовал общую блокировку для синхронизации, используя что-то вроде этого:


         private static final String SHARED_LOCK = "shared_lock";
         private static int i = 0;
         private static int j = 0;
    void increment() {
        synchronized (SHARED_LOCK) {
            i++;
            j++;
        }
    }

И работает нормально.

СЛУЧАЙ II: Теперь то, что я изменил здесь, вместо использования общей блокировки, я подумал об использовании локальной блокировки, выполнив что-то вроде этого:


         private static int i = 0;
         private static int j = 0;
    void increment() {
        final String LOCAL_LOCK = "local_lock";
                  synchronized (LOCAL_LOCK) {
            i++;
            j++;
        }
    }

И я обнаружил, что код все еще работает нормально, то есть синхронизация все еще работает.

СЛУЧАЙ III: Однако, когда я изменил локальный locl на это:

final String LOCAL_LOCK = new String("local_lock");

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

Итак, возвращаясь к моему первоначальному вопросу. Кто-нибудь чувствует, что CASE II не является правильным способом достижения синхронизации? Если да, не могли бы вы также упомянуть, почему?

Заранее спасибо, SacTiw.

Ответы [ 4 ]

8 голосов
/ 11 августа 2010

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

В вашем случае это работает только из-за интернирования String. Если вы использовали final Object LOCAL_LOCK = new Object();, он не будет работать.

Таким образом, блокировки всегда должны быть разделены между потоками.

4 голосов
/ 11 августа 2010

Синхронизация работает правильно, только если вы синхронизируете один и тот же объект между потоками;

  • Первый случай очевиден,
  • Во втором случае компилятор Java обрабатывает строковую константу, поэтому он тоже работает (вы получаете один и тот же объект из пула строк между вызовами),
  • В третьем случае вы форсируете новый строковый объект, и, следовательно, вы получаете собственную частную блокировку для каждого потока, не дающую никакой синхронизации вообще.
3 голосов
/ 11 августа 2010

Здесь очень важно то, что использование «канонизируемых» объектов - таких как, например, String s, которые могут быть интернированы, или Integer констант, которые могут быть общими - в основном открывает для вас возможность использования тот же объект блокировки, что и у другого класса.

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

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

(Я полагаю, что одна из презентаций Java Puzzlers могла бы охватить это; некоторые другие примеры ссылок здесь и здесь )

1 голос
/ 11 августа 2010

Это из-за интернирования строк. Все строки, созданные как в случае 2 с одинаковыми значениями, будут «совместно использовать» один и тот же экземпляр. Строки, созданные как в случае 3, будут разными экземплярами.

Именно эта разница объясняет различное поведение синхронизации, которое вы видите.

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