Ответ
Использование synchronized сопряжено с рисками, связанными с производительностью и корректностью многопоточного кода.
Основные риски:
- Грубая блокировка: Синхронизация метода блокирует весь объект (
this), даже если требуется защитить доступ только к одному полю. - Взаимная блокировка (Deadlock): Возникает, когда два или более потока блокируют разные ресурсы и ожидают освобождения ресурсов, захваченных другими.
- Снижение производительности: Избыточная синхронизация создает contention (состязание) за монитор, что приводит к приостановке потоков и накладным расходам на переключение контекста.
- Отсутствие гибкости: Невозможно попытаться захватить блокировку с таймаутом или прервать ожидание.
Когда использовать:
- Для простых сценариев с короткими критическими секциями.
- Когда альтернативы из
java.util.concurrentизбыточны для задачи.
Пример:
public class Counter {
private int count = 0;
// Безопасно, но блокирует весь экземпляр Counter
public synchronized void increment() {
count++;
}
}
Альтернативы и лучшие практики:
- Атомарные классы (
AtomicInteger,LongAdder): Для одиночных операций над примитивами. - Явные блокировки (
ReentrantLock): Предоставляют больше контроля (таймауты, прерываемость, честность). - Потокобезопасные коллекции (
ConcurrentHashMap,CopyOnWriteArrayList): Вместо синхронизации стандартных коллекций. - Синхронизация по отдельному объекту-замку: Для уменьшения гранулярности блокировки.
private final Object lock = new Object(); public void method() { synchronized(lock) { // Блокируется только lock, а не весь this // ... } }