История вопроса:
С появления Java разработчики столкнулись с проблемой конкурентного доступа к общим ресурсам. Для решения были введены высокоуровневые примитивы синхронизации, главным из которых стал ключевой модификатор synchronized.
Проблема:
Без синхронизации в многопоточных приложениях разделяемые ресурсы могут быть повреждены: появляется data race, состояние объекта становится непредсказуемым.
Решение:
synchronized позволяет организовать монитор для метода или блока кода, обеспечивая доступ только одному потоку в момент времени к критической секции. Синхронизация потока может быть реализована на уровне метода или блока.
Пример блокировки объекта:
public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
Блоки также можно синхронизировать:
public void safeIncrement() { synchronized(this) { count++; } }
Ключевые особенности:
Чем отличается synchronized-метод от synchronized-блока?
synchronized-метод блокирует весь метод для текущего объекта (this) или класса (если метод static). Блок позволяет синхронизировать только нужный кусок кода и выбрать любой объект для блокировки.
Может ли два разных потока одновременно войти в два разных synchronized-метода одного объекта?
Нет, если методы синхронизированы на одном мониторе (this). Если используют разные мониторы, то да.
Влияет ли модификатор synchronized на видимость переменных между потоками?
Да, вход в synchronized-блок сбрасывает кеши потоков и обновляет значения переменных (happens-before relationship).
Разработчик синхронизирует статические методы класса на объекте экземпляра, что не гарантирует корректность при использовании через разные экземпляры.
Плюсы:
Минусы:
Все методы, пользующиеся общим ресурсом, синхронизируются на одном объекте-мониторе, область критической секции минимальна.
Плюсы:
Минусы: