Синхронизированный блок Java для .class

103

Что означает этот код Java? Будет ли он заблокирован на всех объектах MyClass?

synchronized(MyClass.class) {
   //is all objects of MyClass are thread-safe now ??
}

И чем код выше отличается от этого:

synchronized(this) {
   //is all objects of MyClass are thread-safe now ??
}
Андрей Тобилко
источник

Ответы:

144

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

С synchronized(this)блоком охраняется экземпляр. Для каждого экземпляра в блок может войти только один поток.

synchronized(X.class)используется, чтобы убедиться, что в блоке ровно один поток. synchronized(this)гарантирует, что для каждого экземпляра будет ровно один поток. Если это делает фактический код в блоке потокобезопасным, это зависит от реализации. Если мутировать, достаточно только состояния экземпляра synchronized(this).

Томас Юнг
источник
6
«столько потоков может войти в блок, сколько экземпляров» означает, что вторая форма действует как семафор, что не соответствует действительности. Вы должны сказать что-то вроде: «synchronized (this) гарантирует, что только один поток может войти в блок для данного экземпляра класса».
liwp
Исправлено. Я хотел это сказать.
Thomas Jung
2
что такое экземпляр класса по сравнению с экземпляром?
Weishi Zeng
Итак, если у вас есть статический метод, и мы не хотим синхронизировать все его тело, то мы синхронизировали (это) нехорошо, вместо этого уместно synchronized (Foo.class). Это правильно?
krupal.agile
84

Чтобы добавить к другим ответам:

static void myMethod() {
  synchronized(MyClass.class) {
    //code
  }
}

эквивалентно

static synchronized void myMethod() {
  //code
}

и

void myMethod() {
  synchronized(this) {
    //code
  }
}

эквивалентно

synchronized void myMethod() {
  //code
}
Йорн
источник
12
Мне потребовалось второе чтение, чтобы понять, что в первых двух примерах есть ключевое слово static. Просто указываю на это другим, которые, возможно, видели это и пропустили. Без ключевого слова static первые два примера не были бы такими же.
kurtzbot
1
Эти примеры НЕ эквивалентны! Синхронизированные методы «синхронизируются» как дыра, когда поток пытается вызвать методы. С другой стороны, блоки могут иметь код над и под ними, который может выполняться из нескольких потоков. Они синхронизируются только внутри блока! Это не то же самое!
JacksOnF1re
общедоступный статический синглтон getInstance () {if (instance == null) {synchronized (Singleton.class) {instance = new Singleton (); }} возвращаемый экземпляр; }
JacksOnF1re
2
Все дело в том , что там нет никакого кода вне synchronizedблоков. Это делает их эквивалентными. Если вы измените один пример, они действительно уже не те.
Йорн
23

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

Второй получит блокировку только для текущего экземпляра.

Что касается того, делает ли это ваши объекты потокобезопасными, это гораздо более сложный вопрос - нам нужно увидеть ваш код!

Дэвид М
источник
1
да, MyClass.class может быть любой статической переменной и иметь такой же эффект.
pstanton
0

Да, будет (для любого синхронизированного блока / функции).

Я думал над этим вопросом пару дней для себя (на самом деле в котлине). Я наконец нашел хорошее объяснение и хочу им поделиться:

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

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

Плюс заметить, почему .class . Это просто потому, что .classэто эквивалентно любой статической переменной класса, подобного:

private final static Object lock = new Object();

где имя переменной блокировки - это класс, а тип - Class <T>

Подробнее: https://howtodoinjava.com/java/multi-threading/object-vs-class-level-locking/

виталий
источник