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

Какие знаешь инструменты синхронизации параллельного кода?

2.2 Middle🔥 71 комментариев
#Основы Java

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

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

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

Инструменты синхронизации параллельного кода в Java

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

1. synchronized — встроенный механизм блокировки

synchronized — ключевое слово для синхронизации доступа к ресурсам.

// Синхронизированный метод
public class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;  // Только один поток может выполнять одновременно
    }
    
    public synchronized int getCount() {
        return count;
    }
}

// Синхронизированный блок
public class BankAccount {
    private double balance = 1000;
    
    public void withdraw(double amount) {
        synchronized(this) {  // Синхронизация на объекте this
            if (balance >= amount) {
                balance -= amount;
            }
        }
    }
}

// Синхронизация на уровне класса
public class SharedResource {
    private static int value = 0;
    
    public static synchronized void increment() {
        value++;
    }
}

Недостатки:

  • Низкая масштабируемость (всегда монопольная блокировка)
  • Нельзя отменить ожидание
  • Нельзя установить timeout

2. Lock (java.util.concurrent.locks.Lock)

Lock — более гибкая альтернатива synchronized.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();  // Получить блокировку
        try {
            count++;
        } finally {
            lock.unlock();  // ВСЕГДА освобождать в finally
        }
    }
    
    public boolean tryIncrement(long timeout, TimeUnit unit) 
            throws InterruptedException {
        if (lock.tryLock(timeout, unit)) {
            try {
                count++;
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;  // Не получил блокировку
    }
}

Преимущества:

  • timeout поддержка
  • Можно отменить ожидание
  • Более гибко

3. ReadWriteLock — блокировка читатель/писатель

ReadWriteLock позволяет нескольким читателям, но только одному писателю.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class CachedData {
    private String data;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    public String read() {
        lock.readLock().lock();  // Много читателей могут получить это
        try {
            return data;
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public void write(String newData) {
        lock.writeLock().lock();  // Только один писатель
        try {
            this.data = newData;
        } finally {
            lock.writeLock().unlock();
        }
    }
}

// Идеально когда читаний гораздо больше чем записей

4. Semaphore — семафор

Semaphore ограничивает количество потоков, имеющих доступ к ресурсу.

import java.util.concurrent.Semaphore;

public class ConnectionPool {
    private final Semaphore semaphore;
    private final List<Connection> pool;
    
    public ConnectionPool(int maxConnections) {
        this.semaphore = new Semaphore(maxConnections);
        this.pool = new ArrayList<>();
    }
    
    public Connection acquireConnection() throws InterruptedException {
        semaphore.acquire();  // Ждать пока будет доступное место
        synchronized(pool) {
            return pool.remove(0);
        }
    }
    
    public void releaseConnection(Connection conn) {
        synchronized(pool) {
            pool.add(conn);
        }
        semaphore.release();  // Освобождают место
    }
}

// Только 10 потоков могут одновременно получить соединение

5. CountDownLatch — защелка

CountDownLatch позволяет потокам ждать завершения определенного количества операций.

import java.util.concurrent.CountDownLatch;

public class ParallelDownload {
    public static void main(String[] args) throws InterruptedException {
        int numFiles = 5;
        CountDownLatch latch = new CountDownLatch(numFiles);
        
        for (int i = 0; i < numFiles; i++) {
            new Thread(() -> {
                System.out.println("Downloading file...");
                downloadFile();
                latch.countDown();  // Уменьшить счетчик
            }).start();
        }
        
        latch.await();  // Ждать пока счетчик не станет 0
        System.out.println("All files downloaded!");
    }
}

6. CyclicBarrier — циклический барьер

CyclicBarrier позволяет нескольким потокам синхронизироваться в определенной точке.

import java.util.concurrent.CyclicBarrier;

public class MatrixMultiplication {
    private final CyclicBarrier barrier;
    
    public MatrixMultiplication(int numThreads) {
        this.barrier = new CyclicBarrier(numThreads, () -> {
            System.out.println("All threads reached barrier!");
        });
    }
    
    public void computeRow(int row) throws Exception {
        // Вычислить строку
        System.out.println("Thread " + Thread.currentThread().getId() 
            + " computing row " + row);
        
        barrier.await();  // Ждать других потоков
        
        // Продолжить после того как все дошли до барьера
        System.out.println("Thread " + Thread.currentThread().getId() 
            + " continuing");
    }
}

7. Phaser — динамический барьер

Phaser как CyclicBarrier, но динамический (потоки могут добавляться/удаляться).

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1);  // 1 потокожелудающий
        
        for (int i = 1; i <= 3; i++) {
            phaser.register();  // Зарегистрировать еще один поток
            
            new Thread(() -> {
                System.out.println("Phase 1");
                phaser.arriveAndAwaitAdvance();
                
                System.out.println("Phase 2");
                phaser.arriveAndAwaitAdvance();
                
                System.out.println("Phase 3");
                phaser.arriveAndDeregister();  // Покинуть phaser
            }).start();
        }
        
        phaser.arriveAndDeregister();  // Главный поток выходит
    }
}

8. Atomic классы — атомарные переменные

Atomic классы предоставляют потокобезопасные операции без блокировок.

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class AtomicExample {
    private AtomicInteger counter = new AtomicInteger(0);
    private AtomicReference<String> name = new AtomicReference<>("John");
    
    public void increment() {
        counter.incrementAndGet();  // Атомарное увеличение
    }
    
    public int getCount() {
        return counter.get();
    }
    
    public void updateName(String newName) {
        name.set(newName);
    }
    
    public boolean compareAndSwap(String expected, String newValue) {
        return name.compareAndSet(expected, newValue);
    }
}

// Доступные типы:
// - AtomicInteger, AtomicLong
// - AtomicReference, AtomicReferenceArray
// - AtomicBoolean
// - AtomicIntegerFieldUpdater (для полей класса)

9. ConcurrentHashMap — потокобезопасный Map

ConcurrentHashMap позволяет одновременным читателям и писателям работать.

import java.util.concurrent.ConcurrentHashMap;

public class CacheExample {
    private final ConcurrentHashMap<String, String> cache = 
        new ConcurrentHashMap<>();
    
    public void put(String key, String value) {
        cache.put(key, value);
    }
    
    public String get(String key) {
        return cache.get(key);
    }
    
    public String computeIfAbsent(String key) {
        return cache.computeIfAbsent(key, k -> {
            // Вычислить значение если его нет
            return expensiveComputation(k);
        });
    }
}

// Не используй synchronized с ConcurrentHashMap!
// for (String key : cache.keySet()) {  // Может вызвать ConcurrentModificationException
//     cache.remove(key);
// }

10. BlockingQueue — потокобезопасная очередь

BlockingQueue позволяет потокам ждать если очередь пуста/полна.

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class ProducerConsumer {
    private final BlockingQueue<String> queue = 
        new LinkedBlockingQueue<>(10);
    
    public void produce() throws InterruptedException {
        for (int i = 0; i < 20; i++) {
            String item = "Item " + i;
            queue.put(item);  // Ждать если очередь полна
            System.out.println("Produced: " + item);
        }
    }
    
    public void consume() throws InterruptedException {
        while (true) {
            String item = queue.take();  // Ждать если очередь пуста
            System.out.println("Consumed: " + item);
        }
    }
}

11. Сравнение инструментов

ИнструментИспользованиеОсобенности
synchronizedПростая синхронизацияМонопольная блокировка
LockГибкая синхронизацияTimeout, гибкость
ReadWriteLockМного читателейОптимизация для reads
SemaphoreОграничение ресурсовPool-подобное управление
CountDownLatchОжидание завершенияОдноразовое использование
CyclicBarrierСинхронизация точекПереиспользуемый
PhaserДинамическое фазированиеГибкое число потоков
AtomicБез блокировокВысокая производительность
ConcurrentHashMapПотокобезопасный MapЛучше чем synchronized
BlockingQueueProducer-ConsumerВстроенная синхронизация

Итог

Инструменты синхронизации в Java:

  • synchronized — просто для базовых случаев
  • Lock — для сложных сценариев с timeout
  • ReadWriteLock — для read-heavy приложений
  • Semaphore — для ограничения ресурсов
  • CountDownLatch/CyclicBarrier — для координации потоков
  • Atomic — для высокой производительности
  • ConcurrentHashMap — вместо synchronized HashMap
  • BlockingQueue — для паттернов Producer-Consumer

Выбор инструмента зависит от требований и сценария использования.

Какие знаешь инструменты синхронизации параллельного кода? | PrepBro