Почему synchronized-метод на интерфейсе по умолчанию не компилируется

Вопрос звучит просто: можно ли поставить synchronized на default-метод интерфейса? Компилятор отвечает сразу — нет, JLS прямо запрещает модификатор synchronized на методах интерфейса, включая default и static.

Причина не техническая деталь синтаксиса, а концептуальная. У интерфейса нет своего монитора в том смысле, в котором есть у класса — точнее, монитор есть у экземпляра реализующего класса, но интерфейс не знает, каким будет этот класс на момент компиляции. Synchronized требует конкретного объекта-монитора, а интерфейс описывает контракт, а не реализацию с полями и состоянием.

Поэтому если нужна синхронизация внутри default-метода, единственный путь — обычный блок synchronized(this) внутри тела метода, а не модификатор у сигнатуры.

public interface Counter { default void increment() { synchronized (this) { doIncrement(); } }

void doIncrement(); }

Следом спросят: а если несколько классов реализуют этот интерфейс и каждый вызывает increment() параллельно — где именно лок? Правильный ответ: лок на this означает лок на конкретном экземпляре класса-реализации, а не общий для всех реализаций интерфейса. Если нужна общая блокировка на уровне класса, а не инстанса, придётся выносить статический монитор в саму реализацию.

Interface — это контракт без состояния, и synchronized требует состояния. Отсюда и запрет на уровне языка, а не просто договорённость стиля кода.

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки