Какие знаешь примитивы в библиотеке java.util.concurrent?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Примитивы java.util.concurrent
java.util.concurrent — это пакет для многопоточного программирования, предоставляющий потокобезопасные примитивы синхронизации. Эти инструменты гораздо удобнее, чем низкоуровневые synchronized и wait/notify.
1. CountDownLatch
Позволяет потокам ждать, пока другие потоки выполнят определенное количество операций.
CountDownLatch latch = new CountDownLatch(3);
// Поток 1: ждёт
new Thread(() -> {
try {
latch.await(); // Блокируется, пока счётчик не станет 0
System.out.println("Все задачи выполнены!");
} catch (InterruptedException e) {}
}).start();
// Потоки 2-4: уменьшают счётчик
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("Работаю...");
latch.countDown(); // Уменьшить счётчик на 1
}).start();
}
Когда использовать: ожидание завершения параллельных задач (например, инициализация сервиса, ожидание загрузки всех данных).
2. CyclicBarrier
Позволяет группе потоков ждать друг друга в определённой точке (барьере).
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("Все потоки достигли барьера!");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("Поток работает...");
try {
barrier.await(); // Ждёт остальные потоки
} catch (Exception e) {}
System.out.println("Продолжаю после барьера");
}).start();
}
Отличие от CountDownLatch: CyclicBarrier переиспользуется (можно вызвать await() несколько раз), CountDownLatch одноразовый.
3. Semaphore
Ограничивает количество потоков, которые могут одновременно получить доступ к ресурсу.
Semaphore semaphore = new Semaphore(2); // Максимум 2 потока одновременно
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // Получить разрешение
System.out.println(Thread.currentThread().getName() + " работает");
Thread.sleep(2000);
} catch (InterruptedException e) {}
finally {
semaphore.release(); // Отпустить разрешение
}
}).start();
}
Применение: ограничение одновременных HTTP запросов, доступ к ограниченному пулу ресурсов.
4. ReentrantLock
Потокобезопасный замок, более гибкий, чем synchronized. Позволяет:
- Попытаться захватить с таймаутом
- Проверить, не захвачен ли уже
- Использовать
Conditionдля ожидания
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println("Критическая секция");
Thread.sleep(3000);
} finally {
lock.unlock();
}
}).start();
// Попытаться захватить с таймаутом
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// Работаем
} finally {
lock.unlock();
}
}
Преимущества над synchronized:
tryLock()с таймаутом- Проверка захватов:
isLocked(),getHoldCount() - Fairness (справедливое распределение)
5. ReadWriteLock (ReentrantReadWriteLock)
Позволяет множеству потоков одновременно читать, но только одному потоку писать.
ReadWriteLock rwLock = new ReentrantReadWriteLock();
String data = "initial";
// Несколько читателей одновременно
for (int i = 0; i < 3; i++) {
new Thread(() -> {
rwLock.readLock().lock();
try {
System.out.println("Читаю: " + data);
Thread.sleep(2000);
} finally {
rwLock.readLock().unlock();
}
}).start();
}
// Один писатель
new Thread(() -> {
rwLock.writeLock().lock();
try {
data = "updated";
System.out.println("Написал новое значение");
} finally {
rwLock.writeLock().unlock();
}
}).start();
6. Phaser
Регулирует поток потоков через несколько фаз.
Phaser phaser = new Phaser(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("Фаза 1");
phaser.arriveAndAwaitAdvance(); // Ждём остальных
System.out.println("Фаза 2");
phaser.arriveAndAwaitAdvance();
System.out.println("Готово");
phaser.arriveAndDeregister();
}).start();
}
7. Exchanger
Позволяет двум потокам обменяться данными в определённой точке.
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String data1 = "Данные от потока 1";
String data2 = exchanger.exchange(data1);
System.out.println("Получил: " + data2);
} catch (InterruptedException e) {}
}).start();
new Thread(() -> {
try {
String data1 = "Данные от потока 2";
String data2 = exchanger.exchange(data1);
System.out.println("Получил: " + data2);
} catch (InterruptedException e) {}
}).start();
Таблица сравнения примитивов
| Примитив | Назначение | Одноразовый? |
|---|---|---|
| CountDownLatch | Ждать N событий | Да |
| CyclicBarrier | Синхронизация в точке | Нет (переиспользуемый) |
| Semaphore | Ограничить одновременный доступ | Нет |
| ReentrantLock | Взаимное исключение | Нет |
| ReadWriteLock | Много читателей, один писатель | Нет |
| Phaser | Многофазная синхронизация | Нет |
| Exchanger | Обмен данными между двумя потоками | Нет |
Лучшие практики
- ✅ Всегда используй
finallyили try-with-resources для освобождения ресурсов - ✅ Предпочитай эти примитивы вместо
synchronizedиwait/notify - ✅ Избегай deadlock: захватывай ресурсы в одинаковом порядке
- ✅ Используй
tryLock()вместоlock()для избежания бесконечных блокировок
Эти примитивы — основа для построения надежного многопоточного кода в Java.