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

«На каком объекте происходит блокировка при выполнении синхронизированного блока кода в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Синхронизированный блок кода в Java блокируется по монитору объекта, указанного в операторе synchronized.

  • Для синхронизированного метода используется монитор объекта, у которого вызван метод (т.е., this для нестатических методов и объект класса Class для статических).
  • Для синхронизированного блока используется монитор объекта, указанного в скобках: synchronized(object) { ... }.

Пример и объяснение:

public class Counter {
    private final Object lock = new Object(); // Объект-монитор
    private int count = 0;

    public void increment() {
        // Блокировка происходит по монитору объекта 'lock'
        synchronized (lock) {
            count++;
        }
    }
}

Почему это важно:

  1. Взаимоисключение: Только один поток может владеть монитором объекта в данный момент времени.
  2. Видимость изменений: Все изменения, сделанные в синхронизированном блоке, гарантированно становятся видимыми другому потоку после того, как он захватит тот же монитор (обеспечивает happens-before).

Лучшие практики:

  • Используйте отдельный приватный объект (private final Object lock) для блокировки вместо synchronized(this) или синхронизированных методов, чтобы избежать непреднамеренной блокировки внешним кодом.
  • Следите за тем, чтобы не возник deadlock (взаимная блокировка), когда два или более потока ждут мониторы, захваченные друг другом.