← Назад к вопросам

Где используют sinchronized?

2.0 Middle🔥 201 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Где используют synchronized

Synchronized (синхронизация) используется для контроля доступа к общим ресурсам в многопоточной среде. Это механизм для предотвращения race conditions и обеспечения consistency данных.

Когда нужна синхронизация

1. Общие переменные, изменяемые несколькими потоками

public class Counter {
    private int count = 0; // Общая переменная
    
    // БЕЗ synchronized - race condition!
    public void increment() {
        count++; // Три операции: прочитай, прибавь, запиши
    }
    
    // С synchronized - безопасно
    public synchronized void incrementSafe() {
        count++; // Атомарно для других потоков
    }
    
    public synchronized int getCount() {
        return count;
    }
}

Без synchronized другой поток может прочитать count посередине операции increment(), приводя к потере обновлений.

2. Коллекции (List, Map, Set)

Стандартные коллекции не потокобезопасны:

// БЕЗ синхронизации - проблемы при параллельном доступе
List<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    new Thread(() -> list.add("item")).start();
}
// ConcurrentModificationException может быть выброшено

// С synchronized wrapper
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 100; i++) {
    new Thread(() -> syncList.add("item")).start();
}

// ИЛИ используй ConcurrentHashMap, CopyOnWriteArrayList
Map<String, Integer> map = new ConcurrentHashMap<>();
List<String> cowList = new CopyOnWriteArrayList<>();

3. Кэширование и ленивая инициализация

public class Singleton {
    private static Singleton instance = null;
    
    // Double-checked locking паттерн
    public static Singleton getInstance() {
        if (instance == null) { // Первая проверка без блокировки
            synchronized (Singleton.class) { // Блокируем только при необходимости
                if (instance == null) { // Вторая проверка внутри synchronized
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

// Более простой способ в современной Java
public class SimpleSingleton {
    private static final SimpleSingleton instance = new SimpleSingleton();
    
    public static SimpleSingleton getInstance() {
        return instance; // Инициализируется при загрузке класса
    }
}

4. Пулы ресурсов и connection pools

public class ConnectionPool {
    private Queue<Connection> available = new LinkedList<>();
    
    public synchronized Connection getConnection() throws InterruptedException {
        while (available.isEmpty()) {
            wait(); // Ждем, пока освободится соединение
        }
        Connection conn = available.poll();
        notifyAll(); // Уведомляем ждущие потоки
        return conn;
    }
    
    public synchronized void releaseConnection(Connection conn) {
        available.offer(conn);
        notifyAll(); // Пробуждаем один ждущий поток
    }
}

5. Producer-Consumer паттерн

public class Buffer {
    private Queue<String> queue = new LinkedList<>();
    private int maxSize = 10;
    
    public synchronized void produce(String item) throws InterruptedException {
        while (queue.size() >= maxSize) {
            wait(); // Ждем, если буфер полон
        }
        queue.offer(item);
        notifyAll(); // Уведомляем consumer
    }
    
    public synchronized String consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // Ждем, если буфер пуст
        }
        String item = queue.poll();
        notifyAll(); // Уведомляем producer
        return item;
    }
}

6. Счетчики и статистика

public class RequestCounter {
    private int totalRequests = 0;
    private int successfulRequests = 0;
    private int failedRequests = 0;
    
    public synchronized void recordRequest(boolean success) {
        totalRequests++;
        if (success) {
            successfulRequests++;
        } else {
            failedRequests++;
        }
    }
    
    public synchronized void printStats() {
        System.out.println("Total: " + totalRequests);
        System.out.println("Success: " + successfulRequests);
        System.out.println("Failed: " + failedRequests);
    }
}

Уровни синхронизации

Синхронизация методов

public class SyncMethod {
    // Синхронизирует весь метод
    public synchronized void method() {
        // Только один поток может выполнять одновременно
    }
    
    // Это эквивалентно:
    public void methodManual() {
        synchronized (this) {
            // Код
        }
    }
}

Синхронизация блока

public class SyncBlock {
    private List<String> list = new ArrayList<>();
    private int counter = 0;
    
    public void mixedAccess() {
        // Unsynchronized код
        String data = fetchData();
        
        // Только этот блок синхронизирован
        synchronized (this) {
            list.add(data);
            counter++;
        }
        
        // Unsynchronized код
        processResult();
    }
}

Статические synchronized методы

public class StaticSync {
    private static int sharedCounter = 0;
    
    // Синхронизирует весь класс (не инстанс)
    public static synchronized void incrementGlobal() {
        sharedCounter++;
    }
    
    // Эквивалентно:
    public static void incrementGlobalManual() {
        synchronized (StaticSync.class) {
            sharedCounter++;
        }
    }
}

Проблемы с synchronized

1. Deadlock

public class DeadlockExample {
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    
    public void method1() {
        synchronized (lock1) {
            Thread.sleep(100); // Имитируем работу
            synchronized (lock2) { // Может зависнуть
                // Код
            }
        }
    }
    
    public void method2() {
        synchronized (lock2) {
            Thread.sleep(100);
            synchronized (lock1) { // Может зависнуть
                // Код
            }
        }
    }
}

2. Низкая производительность при высокой конкуренции

// При 100+ потоков, synchronized становится узким местом
public class SlowCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
}

// Лучше использовать AtomicInteger
public class FastCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet(); // Неблокирующая операция
    }
}

Когда использовать что

// Простые счетчики → AtomicInteger, AtomicLong
private AtomicInteger counter = new AtomicInteger();

// Коллекции → ConcurrentHashMap, CopyOnWriteArrayList
Map<String, String> map = new ConcurrentHashMap<>();

// Сложная синхронизация → synchronized или ReentrantLock
private final Object lock = new Object();

// Множество потоков с ожиданием → CountDownLatch, Semaphore, Barrier
CountDownLatch latch = new CountDownLatch(10);

// Новые потоки → CompletableFuture, Project Loom (Virtual Threads)
CompletableFuture.runAsync(() -> {
    // Код в другом потоке
});

Вывод

Synchronized используется для:

  • Защиты общих изменяемых данных
  • Обеспечения atomicity операций
  • Реализации паттернов wait/notify
  • Синхронизации доступа к коллекциям

Но в современной Java часто лучше выбрать:

  • Атомарные классы (AtomicInteger, AtomicReference)
  • Потокобезопасные коллекции (ConcurrentHashMap, CopyOnWriteArrayList)
  • Блокирующие очереди (BlockingQueue)
  • Async/Future паттерны (CompletableFuture, Project Loom)
Где используют sinchronized? | PrepBro