В каких случаях используется ключевое слово synchronized в Java?

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

Ответ

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

Основные способы применения:

  1. Синхронизированный метод:

    public synchronized void incrementCounter() {
        counter++;
    }
    • Для нестатического метода блокировка происходит на объекте (this).
    • Для статического метода блокировка происходит на объекте класса (Class).
  2. Синхронизированный блок:

    public void incrementCounter() {
        synchronized(this) { // или любой другой объект-монитор
            counter++;
        }
    }
    • Позволяет синхронизировать только критическую секцию кода, что повышает производительность.
  3. Синхронизация на уровне класса:

    synchronized(MyClass.class) {
        // Критическая секция, работающая со статическими данными
    }

Проблемы и альтернативы:

  • Deadlock: Может возникнуть, если потоки блокируют мониторы в разном порядке.
  • Производительность: Избыточная синхронизация снижает параллелизм.
  • Современные альтернативы: Для сложных сценариев предпочтительнее использовать классы из java.util.concurrent:
    • ReentrantLock для гибкой блокировки.
    • AtomicInteger и другие атомарные классы для простых операций.
    • ConcurrentHashMap для потокобезопасных коллекций.