Для чего используется ключевое слово synchronized в Java?

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

Ответ

Ключевое слово synchronized в Java используется для обеспечения взаимного исключения (mutual exclusion) и синхронизации потоков при доступе к общим ресурсам, предотвращая состояние гонки (race condition).

Оно может применяться двумя способами:

  1. Синхронизированный блок кода:

    public void increment() {
        synchronized (this) { // Монитором служит объект 'this'
            count++; // Критическая секция
        }
    }
    • В скобках указывается объект-монитор, блокировка которого будет захвачена перед выполнением кода в блоке.
  2. Синхронизированный метод:

    public synchronized void decrement() {
        count--; // Весь метод является критической секцией
    }
    • Для нестатического метода монитором служит объект this (экземпляр класса).
    • Для статического метода монитором служит объект класса (Class object).

Как это работает:

  • Чтобы войти в синхронизированный блок или метод, поток должен получить блокировку (lock) на указанном объекте-мониторе.
  • Если блокировка уже удерживается другим потоком, текущий поток переходит в состояние BLOCKED и ждет ее освобождения.
  • Когда поток выходит из синхронизированной области (даже из-за исключения), блокировка автоматически освобождается.

Важные замечания:

  • synchronized гарантирует видимость изменений (реализует happens-before), поэтому изменения, сделанные в синхронизированном блоке, становятся видимыми всем последующим потокам, которые захватят ту же блокировку.
  • Это реентерабельная (reentrant) блокировка: поток, уже владеющий блокировкой, может снова захватить ее (например, при рекурсивном вызове синхронизированного метода).
  • Недостатки: Может приводить к взаимным блокировкам (deadlock) и снижению производительности из-за сериализации выполнения потоков. Для более сложных сценариев часто предпочтительнее использовать примитивы из пакета java.util.concurrent.locks.