← Назад к вопросам
В чем разница между синхронизацией метода и синхронизацией на конкретном объекте?
1.3 Junior🔥 91 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между синхронизацией метода и синхронизацией на конкретном объекте
В Java есть два основных способа синхронизации доступа к общим ресурсам в многопоточной среде: синхронизированные методы и синхронизированные блоки. Они отличаются областью действия, гибкостью и производительностью.
Синхронизация метода
Это простейший способ — добавить ключевое слово synchronized к методу. При этом блокируется весь метод, и доступ к нему получает только один поток одновременно.
public class BankAccount {
private double balance = 1000;
// Синхронизированный метод — блокируется весь метод
public synchronized void withdraw(double amount) {
if (balance >= amount) {
// Весь этот код защищён
balance -= amount;
System.out.println(Thread.currentThread().getName() +
" снял " + amount + ", остаток: " + balance);
}
}
public synchronized double getBalance() {
return balance;
}
}
Особенности синхронизированного метода:
- Блокируется весь метод — даже если критичный код только в части метода
- Блокируется this — для обычных методов блокируется текущий объект (this)
- Для статических методов — блокируется класс (Class объект)
public class SharedResource {
private int counter = 0;
// Синхронизированный обычный метод — блокирует this
public synchronized void increment() {
counter++;
}
// Синхронизированный статический метод — блокирует класс
public static synchronized void staticIncrement() {
// Блокируется SharedResource.class
}
}
Синхронизация на конкретном объекте
Это более гибкий подход — вы можете синхронизировать только нужный блок кода и выбрать, на каком объекте синхронизироваться:
public class BankAccount {
private double balance = 1000;
private final Object balanceLock = new Object(); // Отдельный lock
// Синхронизация на конкретном объекте
public void withdraw(double amount) {
System.out.println(Thread.currentThread().getName() +
" начал операцию");
// Какой-то код без синхронизации
validateAmount(amount);
// Синхронизируем только критичный код
synchronized (balanceLock) {
if (balance >= amount) {
balance -= amount;
System.out.println(Thread.currentThread().getName() +
" снял " + amount + ", остаток: " + balance);
}
}
// Ещё какой-то код без синхронизации
logTransaction(amount);
}
private void validateAmount(double amount) {
// Долгая операция, не требующая синхронизации
Thread.yield();
}
private void logTransaction(double amount) {
// Долгая операция, не требующая синхронизации
Thread.yield();
}
}
Сравнение подходов
public class ComparisonExample {
private int count = 0;
private final Object lock = new Object();
// ❌ Синхронизация метода — весь метод под блокировкой
public synchronized void slowMethod() {
// 1. Долгая операция БЕЗ доступа к общим данным
slowCalculation();
// 2. Критичный код с доступом к count
count++;
// 3. Ещё одна долгая операция
slowIO();
}
// ✅ Синхронизация блока — только критичный код под блокировкой
public void fastMethod() {
// Долгая операция без блокировки
slowCalculation();
// Только критичный код в блокировке
synchronized (lock) {
count++;
}
// Другая долгая операция без блокировки
slowIO();
}
private void slowCalculation() {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
private void slowIO() {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}
Практический пример: несколько независимых блокировок
public class MultiLockExample {
private int balance = 1000;
private int points = 0;
// Отдельные locks для разных ресурсов
private final Object balanceLock = new Object();
private final Object pointsLock = new Object();
// Два потока могут работать одновременно
// (один с balance, другой с points)
public void addBalance(int amount) {
synchronized (balanceLock) {
balance += amount;
}
}
public void addPoints(int amount) {
synchronized (pointsLock) {
points += amount;
}
}
// Это было бы медленнее с synchronized методами
// synchronized void addBalance(int amount) {
// balance += amount;
// }
// synchronized void addPoints(int amount) {
// points += amount;
// }
}
Ключевые отличия
| Аспект | Синхронизированный метод | Синхронизированный блок |
|---|---|---|
| Синтаксис | public synchronized void method() | synchronized (object) { ... } |
| Область блокировки | Весь метод | Только блок кода |
| Гибкость | Низкая | Высокая |
| Производительность | Может быть хуже | Обычно лучше |
| Контроль lock'а | Только this или class | Любой объект |
| Множественные блокировки | Сложно реализовать | Просто реализовать |
Рекомендации
- Используйте синхронизированные блоки — они дают больше контроля
- Минимизируйте область синхронизации — меньше блокировка, больше производительность
- Избегайте вложенных блокировок — риск deadlock'а
- Рассмотрите ReentrantLock — для более сложных сценариев
- Используйте volatile — для простых случаев с одиночным полем
Modern подход (Java 5+)
Для новых проектов лучше использовать java.util.concurrent:
import java.util.concurrent.locks.ReentrantLock;
public class ModernApproach {
private int balance = 1000;
private final ReentrantLock lock = new ReentrantLock();
public void withdraw(int amount) {
lock.lock();
try {
balance -= amount;
} finally {
lock.unlock();
}
}
}