Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Synchronized блок в Java: механизм синхронизации
Synchronized блок — это фундаментальный механизм для обеспечения потокобезопасности в многопоточных приложениях. Он гарантирует, что только один поток может выполнять код внутри блока одновременно.
Основное назначение
Synchronized блок нужен для:
- Защиты от race conditions — когда несколько потоков одновременно обращаются к общему ресурсу
- Обеспечения atomicity — операции выполняются неделимо
- Гарантирования visibility — изменения, сделанные одним потоком, видны другим
- Сохранения консистентности данных — предотвращение корупции состояния
Как работает synchronized
Synchronized использует монитор (intrinsic lock) каждого объекта. Когда поток входит в synchronized блок:
- Проверяет, свободен ли монитор объекта
- Если да — захватывает его и входит в блок
- Другие потоки ждут в очереди
- После выхода — освобождает монитор
// Синхронизация по конкретному объекту
private final Object lock = new Object();
private int counter = 0;
public void incrementCounter() {
synchronized (lock) { // только один поток одновременно
counter++; // критическая секция
}
}
Варианты использования
1. Синхронизированные методы
public class Counter {
private int count = 0;
// Монитор — сам объект (this)
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
2. Синхронизированные блоки (тоньше контроль)
public class BankAccount {
private double balance = 0;
private List<String> transactions = new ArrayList<>();
public void transfer(double amount) {
// Синхронизируем только самое важное
synchronized (this) {
balance -= amount;
}
// Дорогая операция вне synchronized
logTransaction("transfer", amount);
}
}
3. Синхронизация по разным объектам (для параллелизма)
public class Wallet {
private final Object incomeLock = new Object();
private final Object expenseLock = new Object();
private double income = 0;
private double expenses = 0;
public void addIncome(double amount) {
synchronized (incomeLock) { // не блокирует expenses
income += amount;
}
}
public void addExpense(double amount) {
synchronized (expenseLock) { // не блокирует income
expenses += amount;
}
}
}
4. Синхронизация статических методов
public class GlobalCounter {
private static int count = 0;
private static final Object lock = new Object();
// Монитор — сам класс (GlobalCounter.class)
public static synchronized void increment() {
count++;
}
// Эквивалентно:
public static void incrementManual() {
synchronized (GlobalCounter.class) {
count++;
}
}
}
Типичные ошибки
1. Забывчивость про synchronized при доступе
// ❌ ОПАСНО: забыли synchronized при чтении
public int unsafeRead() {
return count; // может прочитать неполное значение
}
// ✅ ПРАВИЛЬНО: оба метода synchronized
public synchronized int getCount() {
return count;
}
2. Синхронизация по неправильному объекту
// ❌ ОПАСНО: каждый раз новый объект
Object lock = new Object(); // создаётся в методе!
synchronized (lock) { ... } // каждый раз разные потоки захватывают разные мониторы
// ✅ ПРАВИЛЬНО: final field
private final Object lock = new Object();
3. Deadlock — взаимная блокировка
// ❌ ОПАСНО: может привести к deadlock
Object lock1 = new Object();
Object lock2 = new Object();
// Поток A: захватывает lock1, ждёт lock2
synchronized (lock1) {
synchronized (lock2) { ... }
}
// Поток B: захватывает lock2, ждёт lock1
synchronized (lock2) {
synchronized (lock1) { ... }
} // DEADLOCK! Оба потока ждут друг друга
Альтернативы synchronized
Для современного Java (5+) существуют более гибкие инструменты:
// ReentrantLock — явное управление
private final Lock lock = new ReentrantLock();
public void someMethod() {
lock.lock();
try {
// критическая секция
} finally {
lock.unlock();
}
}
// AtomicInteger — для простых счётчиков
private final AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet();
}
// ConcurrentHashMap — потокобезопасная коллекция
Map<String, String> map = new ConcurrentHashMap<>();
map.put("key", "value"); // потокобезопасна внутри
Производительность
Synchronized — это дорогая операция:
- Захват/освобождение монитора требует time
- Другие потоки блокируются
- JVM не может оптимизировать код внутри synchronized
Best practices:
- Держи synchronized блоки как можно короче
- Синхронизируй только критические секции
- Рассмотри современные альтернативы (Lock, Atomic, Concurrent*)
- Для высоконагруженных систем используй lock-free структуры
Заключение
Synchronized блок — это базовый, но мощный инструмент для защиты потокобезопасности. Понимание механики монитора, правильное применение и знание альтернатив — критичные навыки для Java developer, работающего с многопоточностью.