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

На каком объекте происходит блокировка при выполнении синхронизированного метода

2.0 Middle🔥 211 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Синхронизация в Java: объект блокировки

При выполнении синхронизированного метода блокировка происходит на самом объекте (this) для методов экземпляра, или на классе (Class<?> объекте) для статических методов.

Синхронизированный метод экземпляра

Блокировка на самом объекте (this):

public class Counter {
    private int count = 0;
    
    // Эквивалентны:
    // Способ 1: синхронизированный метод
    public synchronized void increment() {
        count++;  // Блокировка на this
    }
    
    // Способ 2: явный блок синхронизации
    public void incrementExplicit() {
        synchronized (this) {  // Явная блокировка на this
            count++;
        }
    }
}

// Использование:
Counter counter = new Counter();

// Два потока конкурируют за одну блокировку (на объекте counter)
Thread t1 = new Thread(() -> counter.increment());
Thread t2 = new Thread(() -> counter.increment());

t1.start();
t2.start();

Статический синхронизированный метод

Блокировка на объекте Class:

public class StaticCounter {
    private static int count = 0;
    
    // Блокировка происходит на StaticCounter.class
    public static synchronized void increment() {
        count++;  // Блокировка на StaticCounter.class
    }
    
    // Эквивалентно:
    public static void incrementExplicit() {
        synchronized (StaticCounter.class) {  // Явная блокировка на класс
            count++;
        }
    }
}

Визуализация блокировки

┌─────────────────┐
│  Counter obj    │
├─────────────────┤
│  count = 0      │
│  (monitor)  ┌─┐ │  <- Монитор объекта
│             └─┘ │     Thread может владеть
└─────────────────┘

Если объект синхронизирован:
(monitor) = LOCKED

Различие между методом и статическим методом

public class Demo {
    private int instanceValue = 0;
    private static int staticValue = 0;
    
    // Каждый ОБЪЕКТ имеет свой монитор
    public synchronized void instanceMethod() {
        instanceValue++;
    }
    
    // КЛАСС имеет монитор (один на всех)
    public static synchronized void staticMethod() {
        staticValue++;
    }
}

// Демонстрация
Demo obj1 = new Demo();
Demo obj2 = new Demo();

// ✅ Разные объекты = разные блокировки!
// Эти потоки НЕ будут ждать друг друга
Thread t1 = new Thread(obj1::instanceMethod);
Thread t2 = new Thread(obj2::instanceMethod);

// ❌ Статические методы = общая блокировка!
// Эти потоки БУДУТ ждать друг друга
Thread t3 = new Thread(Demo::staticMethod);
Thread t4 = new Thread(Demo::staticMethod);

Явная синхронизация для контроля

public class ExplicitLocking {
    private int value = 0;
    
    // ❌ Блокировка на this — все методы конкурируют
    public synchronized void methodA() {
        // медленная операция
    }
    
    public synchronized void methodB() {
        // медленная операция
    }
    
    // ✅ Правильно: разные блокировки = параллелизм
    private final Object lockA = new Object();
    private final Object lockB = new Object();
    
    public void methodA_Better() {
        synchronized (lockA) {
            // медленная операция
        }
    }
    
    public void methodB_Better() {
        synchronized (lockB) {
            // медленная операция (параллельна methodA_Better)
        }
    }
}

Практический пример: счётчик потокобезопасный

public class ThreadSafeCounter {
    private int count = 0;
    
    // Блокировка происходит на этом объекте (this)
    public synchronized void increment() {
        count++;  // Критическая секция
    }
    
    public synchronized int getCount() {
        return count;  // Блокировка на this
    }
}

// Использование
public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadSafeCounter counter = new ThreadSafeCounter();
        
        // 10 потоков, каждый увеличивает счётчик 1000 раз
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();  // Все конкурируют за монитор
                }
            });
            threads[i].start();
        }
        
        // Ждём завершения всех потоков
        for (Thread t : threads) {
            t.join();
        }
        
        System.out.println(counter.getCount());  // 10000 ✅ (не race condition)
    }
}

Что такое монитор (Monitor)

Каждый объект в Java имеет встроенный монитор (invisible lock):

public class Object {
    // Каждый объект имеет монитор
    private Object monitor;  // Внутренний монитор
    
    // Можно только захватить/отпустить через synchronized
    public void wait() { ... }     // Отпустить монитор и ждать
    public void notify() { ... }   // Пробудить один ожидающий
    public void notifyAll() { ... } // Пробудить всех
}

Несколько потоков и одна блокировка

public class MultiThreadExample {
    private int value = 0;
    
    public synchronized void criticalSection(String threadName) {
        System.out.println(threadName + " захватил монитор");
        value++;
        
        try {
            Thread.sleep(1000);  // Долгая операция
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println(threadName + " отпускает монитор");
    }
}

// Вывод:
// Thread-0 захватил монитор
// Thread-0 отпускает монитор
// Thread-1 захватил монитор
// Thread-1 отпускает монитор
// Thread-2 захватил монитор
// Thread-2 отпускает монитор
// (Поток за потоком, не параллельно!)

Иерархия блокировок

public class LockHierarchy {
    private int value = 0;
    private final Object monitor = new Object();
    
    // Можно вложить синхронизацию
    public void outerLock() {
        synchronized (this) {  // Блокировка 1
            synchronized (monitor) {  // Блокировка 2
                value++;  // Находимся под двумя блокировками
            }
        }
    }
    
    // ⚠️ Опасность: deadlock
    public void potentialDeadlock1() {
        synchronized (this) {
            synchronized (monitor) {  // Порядок 1-2
                value++;
            }
        }
    }
    
    public void potentialDeadlock2() {
        synchronized (monitor) {
            synchronized (this) {  // Порядок 2-1 - РАЗНЫЙ!
                value++;  // Может быть deadlock!
            }
        }
    }
}

Современные альтернативы

import java.util.concurrent.locks.ReentrantLock;

public class ModernLocking {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    // Больше контроля, чем synchronized
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();  // Гарантированно отпустит блокировку
        }
    }
    
    // Ещё лучше: используй synchronized (Java 5+)
    // Или java.util.concurrent.atomic
    public void incrementAtomic() {
        // AtomicInteger thread-safe без явных блокировок
    }
}

Вывод

  • Методы экземпляра: блокировка на this объекте
  • Статические методы: блокировка на Class<?> объекте
  • Явная синхронизация: synchronized (object) блокирует на object
  • Один объект = один монитор (встроенная блокировка)
  • ⚠️ Deadlock: правильно упорядочивайте несколько блокировок
  • Альтернативы: ReentrantLock, AtomicInteger, ConcurrentHashMap