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

Какие знаешь способы синхронизации в Java?

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

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

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

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

Способы синхронизации в Java: Полный обзор

Синхронизация — это механизм обеспечения потокобезопасности при доступе нескольких потоков к общим ресурсам. Java предоставляет множество способов решить эту проблему, от примитивных до высокоуровневых.

1. Synchronized блок (Monitor/Intrinsic Lock)

Базовый механизм синхронизации через мониторы объектов:

public class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public void safeIncrement() {
        synchronized (this) {
            count++;
        }
    }
    
    private final Object lock = new Object();
    public void criticalSection() {
        synchronized (lock) {
            count++;
        }
    }
}

Плюсы: Просто использовать, JVM оптимизирует. Минусы: Грубый механизм, риск deadlock'а.

2. Volatile (Видимость без блокировок)

Гарантирует видимость изменений между потоками БЕЗ блокировок:

public class FlagService {
    private volatile boolean running = true;
    
    public void stopService() {
        running = false;
    }
    
    public void doWork() {
        while (running) {
            processWork();
        }
    }
}

Плюсы: Нет блокировок, высокая производительность. Минусы: Не гарантирует atomicity, только видимость.

3. Atomic классы (Атомарные операции)

Атомарные операции без явного synchronized:

import java.util.concurrent.atomic.*;

public class AtomicCounter {
    private AtomicInteger counter = new AtomicInteger(0);
    
    public void increment() {
        counter.incrementAndGet();
    }
    
    public int get() {
        return counter.get();
    }
    
    public boolean compareAndSet(int expect, int update) {
        return counter.compareAndSet(expect, update);
    }
}

Доступные типы: AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference.

Плюсы: Высокая производительность (lock-free), простой API. Минусы: Только для простых операций.

4. ReentrantLock (Явное управление)

Мощнее чем synchronized:

import java.util.concurrent.locks.*;

public class LockingCounter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    
    public void incrementWithTimeout() throws InterruptedException {
        if (lock.tryLock(5, TimeUnit.SECONDS)) {
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }
    }
}

Плюсы: Явное управление, timeout поддержка. Минусы: Сложнее использовать.

5. ReadWriteLock (Для оптимизации чтений)

private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

public int getValue() {
    rwLock.readLock().lock();
    try {
        return count;
    } finally {
        rwLock.readLock().unlock();
    }
}

public void setValue(int newValue) {
    rwLock.writeLock().lock();
    try {
        count = newValue;
    } finally {
        rwLock.writeLock().unlock();
    }
}

6. Condition (Условные переменные)

Синхронизация через условия (producer-consumer):

import java.util.concurrent.locks.*;

public class ProducerConsumerQueue<T> {
    private final Queue<T> queue = new LinkedList<>();
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    
    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (queue.isFull()) {
                notFull.await();
            }
            queue.add(item);
            notEmpty.signalAll();
        } finally {
            lock.unlock();
        }
    }
    
    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();
            }
            T item = queue.poll();
            notFull.signalAll();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

7. Semaphore (Семафор)

Ограничивает количество потоков:

import java.util.concurrent.*;

public class PoolManager {
    private final Semaphore poolSemaphore = new Semaphore(5);
    
    public void useConnection() throws InterruptedException {
        poolSemaphore.acquire();
        try {
            Connection conn = borrowConnection();
            executeQuery(conn);
        } finally {
            poolSemaphore.release();
        }
    }
}

8. CountDownLatch (Барьер)

Синхронизация нескольких потоков:

import java.util.concurrent.*;

public class TaskExecutor {
    public void parallelExecution() throws InterruptedException {
        int taskCount = 5;
        CountDownLatch latch = new CountDownLatch(taskCount);
        
        for (int i = 0; i < taskCount; i++) {
            new Thread(() -> {
                try {
                    doWork();
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        latch.await();
        System.out.println("Все задачи выполнены!");
    }
}

9. CyclicBarrier (Переиспользуемый барьер)

public class ParallelPhases {
    public void multiPhaseTask() throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(3);
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println("Фаза 1 - работа");
                    barrier.await();
                    System.out.println("Фаза 2 - работа");
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {}
            }).start();
        }
    }
}

10. Потокобезопасные коллекции

import java.util.concurrent.*;

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
DelayQueue<Task> delayedQueue = new DelayQueue<>();

11. Executor Framework

Высокоуровневое управление потоками:

import java.util.concurrent.*;

ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        System.out.println("Задача выполняется");
    });
}

executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);

Таблица сравнения

СпособИспользованиеПроизводительностьСложность
synchronizedПростые случаиСредняяНизкая
volatileФлагиОчень высокаяНизкая
Atomic*СчётчикиВысокаяНизкая
ReentrantLockГибкий контрольВысокаяСредняя
ReadWriteLockМного чтенийВысокаяСредняя
SemaphoreПул ресурсовСредняяСредняя
BlockingQueueProducer-consumerВысокаяНизкая
ExecutorУправление потокамиВысокаяНизкая

Best Practices

Предпочитай высокоуровневые инструменты (BlockingQueue, Executor). Минимизируй scope блокировки — только критические операции внутри synchronized. Избегай Object.notify() и Object.wait(), используй Condition вместо этого.

Заключение

Java предоставляет богатый набор инструментов синхронизации. Выбор зависит от задачи: для простых случаев — synchronized, для производительности — Atomic и ConcurrentHashMap, для сложной координации — Lock и Condition, для управления потоками — Executor Framework.

Какие знаешь способы синхронизации в Java? | PrepBro