Какие знаешь инструменты синхронизации параллельного кода?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Инструменты синхронизации параллельного кода в 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 |
| BlockingQueue | Producer-Consumer | Встроенная синхронизация |
Итог
Инструменты синхронизации в Java:
- synchronized — просто для базовых случаев
- Lock — для сложных сценариев с timeout
- ReadWriteLock — для read-heavy приложений
- Semaphore — для ограничения ресурсов
- CountDownLatch/CyclicBarrier — для координации потоков
- Atomic — для высокой производительности
- ConcurrentHashMap — вместо synchronized HashMap
- BlockingQueue — для паттернов Producer-Consumer
Выбор инструмента зависит от требований и сценария использования.