← Назад к вопросам

В чем разница между синхронизацией метода и синхронизацией на конкретном объекте?

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;
    }
}

Особенности синхронизированного метода:

  1. Блокируется весь метод — даже если критичный код только в части метода
  2. Блокируется this — для обычных методов блокируется текущий объект (this)
  3. Для статических методов — блокируется класс (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Любой объект
Множественные блокировкиСложно реализоватьПросто реализовать

Рекомендации

  1. Используйте синхронизированные блоки — они дают больше контроля
  2. Минимизируйте область синхронизации — меньше блокировка, больше производительность
  3. Избегайте вложенных блокировок — риск deadlock'а
  4. Рассмотрите ReentrantLock — для более сложных сценариев
  5. Используйте 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();
        }
    }
}
В чем разница между синхронизацией метода и синхронизацией на конкретном объекте? | PrepBro