← Назад к вопросам
Что является монитором для статического synchronized метода
2.2 Middle🔥 191 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что является монитором для статического synchronized метода
Краткий ответ
Монитором для статического synchronized метода является объект Class (класс сам по себе), а не экземпляр класса. В Java каждый класс имеет свой Class объект, который служит монитором для синхронизации статических методов.
Различие между обычным и статическим synchronized
Обычный (instance) synchronized метод:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
// Монитор: конкретный экземпляр this
}
}
// Разные объекты - разные мониторы
Counter counter1 = new Counter();
Counter counter2 = new Counter();
// counter1.increment() и counter2.increment() могут выполняться параллельно
// Потому что они синхронизируются на разные объекты
thread1.start(() -> counter1.increment());
thread2.start(() -> counter2.increment());
Статический synchronized метод:
public class Counter {
private static int count = 0;
public static synchronized void increment() {
count++;
// Монитор: Counter.class (объект Class)
}
}
// Всегда один и тот же монитор
// Counter.increment() и Counter.increment() НЕ могут выполняться параллельно
// Потому что они синхронизируются на один объект Counter.class
thread1.start(() -> Counter.increment());
thread2.start(() -> Counter.increment());
Объект Class как монитор
Что такое Class объект:
В Java каждый класс имеет свой объект Class, который создаётся загрузчиком классов:
public class MyClass {
public static void test() {
// Получить объект Class для MyClass
Class<?> clazz1 = MyClass.class;
Class<?> clazz2 = MyClass.class;
// Это ОДИН и ТОТ ЖЕ объект
System.out.println(clazz1 == clazz2); // true
}
}
// Также можно получить через getClass() на экземпляре
MyClass instance1 = new MyClass();
MyClass instance2 = new MyClass();
Class<?> clazz1 = instance1.getClass();
Class<?> clazz2 = instance2.getClass();
System.out.println(clazz1 == clazz2); // true - это один объект Class
Практический пример
Статический synchronized монитор:
public class BankAccount {
private static int totalBalance = 1000;
// Это синхронизируется на BankAccount.class
public static synchronized void transfer(int amount) {
totalBalance -= amount;
System.out.println("Переведено " + amount + ", остаток: " + totalBalance);
}
}
// Использование:
class TransferThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
BankAccount.transfer(1);
}
}
}
public static void main(String[] args) throws InterruptedException {
// Создаём несколько потоков
Thread t1 = new TransferThread();
Thread t2 = new TransferThread();
Thread t3 = new TransferThread();
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
// Итоговый баланс: 700 (1000 - 300)
// Это возможно ТОЛЬКО потому что все потоки синхронизируются на один монитор
}
Явное использование synchronized(class)
Вы можете явно синхронизироваться на объект Class:
public class Example {
private static int counter = 0;
// Способ 1: synchronized метод
public static synchronized void increment1() {
counter++;
}
// Способ 2: явно synchronized(Class)
public static void increment2() {
synchronized (Example.class) {
counter++;
// Эквивалент способу 1
}
}
// Способ 3: синхронизация на getClass()
public void increment3() {
synchronized (this.getClass()) {
counter++; // Синхронизируется на Example.class
}
}
}
Сравнение мониторов
public class SyncExample {
private int instanceVar = 0;
private static int staticVar = 0;
// 1. Обычный synchronized - монитор: this (текущий экземпляр)
public synchronized void instanceMethod() {
instanceVar++;
// Монитор: this
}
// 2. Статический synchronized - монитор: SyncExample.class
public static synchronized void staticMethod() {
staticVar++;
// Монитор: SyncExample.class
}
// 3. Явный монитор на this
public void explicitInstance() {
synchronized (this) {
instanceVar++;
// Эквивалент instanceMethod()
}
}
// 4. Явный монитор на Class
public static void explicitStatic() {
synchronized (SyncExample.class) {
staticVar++;
// Эквивалент staticMethod()
}
}
}
// Визуализация:
// Instance method синхронизируется на: obj1, obj2, obj3 (разные мониторы)
// Static method синхронизируется на: SyncExample.class (один монитор)
Важный пример: как это может привести к проблемам
public class Cache {
private static Map<String, String> data = new HashMap<>();
// ОПАСНО: если не осторожен с синхронизацией
public void put(String key, String value) {
// Это НЕ синхронизируется со статическим методом get()
data.put(key, value);
// Монитор: this (конкретный экземпляр)
}
public static synchronized String get(String key) {
return data.get(key);
// Монитор: Cache.class
}
}
// ПРОБЛЕМА:
Cache cache1 = new Cache();
Cache cache2 = new Cache();
// Два разных монитора!
thread1.start(() -> cache1.put("key", "value")); // Монитор: cache1
thread2.start(() -> Cache.get("key")); // Монитор: Cache.class
// Race condition! HashMap не thread-safe!
Правильное решение
public class Cache {
private static Map<String, String> data = new HashMap<>();
// Вариант 1: оба статические synchronized
public static synchronized void put(String key, String value) {
data.put(key, value); // Монитор: Cache.class
}
public static synchronized String get(String key) {
return data.get(key); // Монитор: Cache.class
}
}
// Вариант 2: явная синхронизация на Class
public class Cache {
private static Map<String, String> data = new HashMap<>();
public void put(String key, String value) {
synchronized (Cache.class) {
data.put(key, value);
}
}
public String get(String key) {
synchronized (Cache.class) {
return data.get(key);
}
}
}
// Вариант 3: использование ConcurrentHashMap (лучший вариант)
public class Cache {
private static Map<String, String> data = new ConcurrentHashMap<>();
public static void put(String key, String value) {
data.put(key, value);
}
public static String get(String key) {
return data.get(key);
}
}
Таблица: Мониторы для разных типов synchronized
| Код | Монитор | Видимость |
|---|---|---|
synchronized void method() | this | На все экземпляры |
synchronized static void method() | ClassName.class | На весь класс |
synchronized(this) {} | this | На экземпляр |
synchronized(ClassName.class) {} | ClassName.class | На весь класс |
synchronized(объект) {} | Любой объект | На этот объект |
Визуализация потоков
Два обычных synchronized метода:
threads: [T1] [T2] [T3] [T4]
objects: obj1 obj1 obj2 obj2
monitor: m1 m1 m2 m2
|____| |____| <- Две независимые группы
Два статических synchronized метода:
threads: [T1] [T2] [T3] [T4]
objects: - - - - (неприменимо)
monitor: Class Class Class Class (один монитор)
|_________________________| <- Все потоки конкурируют за один монитор
Заключение
Для статического synchronized метода монитором является объект Class самого класса. Это означает:
- Все потоки, выполняющие статические synchronized методы одного класса, синхронизируются на один и тот же монитор
- Статические synchronized методы конкурируют друг с другом за доступ
- Это отличается от обычных synchronized методов, которые синхронизируются на каждый экземпляр отдельно
- При необходимости вы можете явно синхронизировать код на Class объект, используя
synchronized(ClassName.class)