← Назад к вопросам
На что синхронизируешься при добавлении synchronized на метод?
2.0 Middle🔥 251 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
На что синхронизируешься при добавлении synchronized на метод?
Краткий ответ
При добавлении synchronized на метод, ты синхронизируешься на объект (instance), если метод обычный, или на класс (Class объект), если метод static. Это означает, что в один момент времени только один поток может выполнять синхронизированный метод этого объекта.
Виды synchronized методов
// ✅ Instance synchronized method
public synchronized void instanceMethod() {
// Синхронизируется на объект (this)
// Только один поток может выполнять этот метод на одном объекте
}
// ✅ Static synchronized method
public static synchronized void staticMethod() {
// Синхронизируется на Class объект (MyClass.class)
// Только один поток может выполнять этот метод на весь класс
}
// ✅ Синхронизированный блок на объект
public void methodWithSyncBlock() {
synchronized(this) {
// Синхронизируется на объект (this)
}
}
// ✅ Синхронизированный блок на класс
public void methodWithClassSync() {
synchronized(MyClass.class) {
// Синхронизируется на Class объект
}
}
1. Instance synchronized method (синхронизация на объект)
Как это работает:
public class Counter {
private int count = 0;
// Синхронизируется на объект 'this'
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class Example {
public static void main(String[] args) {
Counter counter1 = new Counter(); // объект 1
Counter counter2 = new Counter(); // объект 2
// Разные объекты = разные мониторы (блокировки)
// Поэтому эти методы могут выполняться одновременно:
Thread t1 = new Thread(() -> counter1.increment()); // блокирует monitor объекта counter1
Thread t2 = new Thread(() -> counter2.increment()); // блокирует monitor объекта counter2
t1.start();
t2.start(); // Оба выполнятся одновременно!
// Но на одном объекте:
Thread t3 = new Thread(() -> counter1.increment()); // хочет блокировку counter1
Thread t4 = new Thread(() -> counter1.getCount()); // хочет блокировку counter1
t3.start();
t4.start(); // Один будет ждать другого!
}
}
Визуализация:
Время 0:
Counter obj1 { Counter obj2 {
synchronized synchronized
monitor=LOCKED monitor=FREE
выполняет increment готов к выполнению
} }
Время 1:
Counter obj1 { Counter obj2 {
synchronized synchronized
monitor=FREE monitor=LOCKED
готов выполняет increment
} }
Время 2:
Counter obj1 { Counter obj2 {
synchronized synchronized
monitor=LOCKED monitor=FREE
выполняет getCount готов
} }
2. Static synchronized method (синхронизация на Class)
Как это работает:
public class Logger {
private static int logCount = 0;
// Синхронизируется на Logger.class
public static synchronized void log(String message) {
logCount++;
System.out.println(message);
}
public static synchronized int getLogCount() {
return logCount;
}
}
public class Example {
public static void main(String[] args) {
Logger logger1 = new Logger(); // объект 1
Logger logger2 = new Logger(); // объект 2
// Разные объекты, но static synchronized синхронизируется на класс!
// Поэтому они не могут выполняться одновременно:
Thread t1 = new Thread(() -> logger1.log("Message 1")); // хочет блокировку Logger.class
Thread t2 = new Thread(() -> logger2.log("Message 2")); // хочет блокировку Logger.class
t1.start();
t2.start(); // Один будет ждать другого! (даже на разных объектах)
// Потому что есть только ОДНА блокировка на класс Logger
}
}
Визуализация:
Vreme 0:
Logger.class { logger1 { logger2 {
monitor=LOCKED объект объект
выполняет log()
} } }
Время 1:
Logger.class { logger1 { logger2 {
monitor=LOCKED объект объект
выполняет getLogCount()
} } }
Время 2:
Logger.class { logger1 { logger2 {
monitor=FREE объект объект
готов
} } }
3. Разница между instance и static synchronized
public class BankAccount {
private static int totalBalance = 0; // Общий баланс всех счетов
private int balance = 0; // Баланс этого счета
// Синхронизируется на объект (this)
public synchronized void deposit(int amount) {
balance += amount; // Только этот счет блокируется
}
// Синхронизируется на класс
public static synchronized void addToTotalBalance(int amount) {
totalBalance += amount; // Весь класс блокируется
}
public synchronized int getBalance() {
return balance;
}
public static synchronized int getTotalBalance() {
return totalBalance;
}
}
public class Example {
public static void main(String[] args) throws InterruptedException {
BankAccount account1 = new BankAccount();
BankAccount account2 = new BankAccount();
// Эти два потока могут выполняться одновременно
// (разные объекты, разные мониторы):
Thread t1 = new Thread(() -> account1.deposit(100));
Thread t2 = new Thread(() -> account2.deposit(200));
// Но эти два потока NOT одновременно
// (один класс, один монитор):
Thread t3 = new Thread(() -> BankAccount.addToTotalBalance(100));
Thread t4 = new Thread(() -> BankAccount.addToTotalBalance(200));
}
}
Как работает монитор (Monitor)
Каждый объект и класс имеют встроенный монитор:
public class MonitorExample {
private int value = 0;
public synchronized void method1() {
// Шаг 1: Поток пытается захватить монитор объекта
// Шаг 2: Если монитор свободен — захватывает его и входит
// Если занят — ждёт в очереди
value++;
// Шаг 3: После выхода из метода монитор освобождается
}
public synchronized void method2() {
// Этот метод также ждёт ТОГО ЖЕ монитора
// Потому что синхронизируется на тот же объект
value--;
}
public void unsynchronizedMethod() {
// Этот метод НЕ требует монитор
// Несколько потоков могут выполнять его одновременно
System.out.println(value);
}
}
Эквивалентность synchronized методов и блоков
// ✅ Эти два варианта ЭКВИВАЛЕНТНЫ для instance методов:
// Вариант 1: synchronized метод
public synchronized void instanceMethod() {
// код
}
// Вариант 2: synchronized блок с 'this'
public void instanceMethod() {
synchronized(this) {
// код
}
}
// ✅ Эти два варианта ЭКВИВАЛЕНТНЫ для static методов:
// Вариант 1: static synchronized метод
public static synchronized void staticMethod() {
// код
}
// Вариант 2: synchronized блок с классом
public static void staticMethod() {
synchronized(MyClass.class) {
// код
}
}
Проблема: Deadlock при неправильной синхронизации
// ❌ Опасно: может привести к deadlock
public class BankTransfer {
public synchronized void transferFrom(BankAccount other, int amount) {
// Поток 1: заблокировал this, хочет заблокировать other
other.withdraw(amount);
}
public synchronized void withdraw(int amount) {
// Может ждать, пока this будет разблокирован
}
}
// Сценарий deadlock'а:
// Поток 1: account1.transferFrom(account2, 100)
// - блокирует account1
// - хочет заблокировать account2 (вызов account2.withdraw)
// Поток 2: account2.transferFrom(account1, 100)
// - блокирует account2
// - хочет заблокировать account1 (вызов account1.withdraw)
// DEADLOCK! Оба потока ждут друг друга
// ✅ Решение: использовать synchronized блоки в одном порядке
public void transferFrom(BankAccount other, int amount) {
BankAccount first, second;
if (System.identityHashCode(this) < System.identityHashCode(other)) {
first = this;
second = other;
} else {
first = other;
second = this;
}
synchronized(first) {
synchronized(second) {
// Теперь всегда блокируем в одном порядке
this.balance -= amount;
other.balance += amount;
}
}
}
Проверка синхронизации в Java
public class SyncCheck {
public static void main(String[] args) {
Counter counter = new Counter();
// Проверяем, что объект может быть использован в synchronized
System.out.println("Object hashCode: " + System.identityHashCode(counter));
// Thread.holdsLock проверяет, есть ли у потока блокировка
System.out.println("Holds lock: " + Thread.holdsLock(counter));
Thread t = new Thread(() -> {
synchronized(counter) {
System.out.println("Inside sync: " + Thread.holdsLock(counter));
}
System.out.println("After sync: " + Thread.holdsLock(counter));
});
t.start();
}
}
Таблица: что синхронизируется
| Тип | На что синхронизируется | Область влияния |
|---|---|---|
synchronized void method() | На объект (this) | Только этот объект |
static synchronized void method() | На класс (Class) | Весь класс |
synchronized(this) { } | На объект (this) | Только блок |
synchronized(obj) { } | На объект (obj) | Только блок |
synchronized(MyClass.class) { } | На класс (MyClass) | Только блок |
Заключение
При синхронизации на методе:
-
Instance synchronized — синхронизируется на объект (this)
- Разные объекты могут выполняться параллельно
- Разные методы одного объекта не могут выполняться параллельно
-
Static synchronized — синхронизируется на класс (MyClass.class)
- Даже разные объекты не могут выполняться параллельно
- Только один поток на весь класс
-
Монитор — встроенный механизм блокировки в каждом объекте и классе
-
Deadlock — возможен, если неправильно комбинировать синхронизированные методы