Please open Telegram to view this post
VIEW IN TELEGRAM
CountDownLatch — это синхронизатор из пакета java.util.concurrent, который позволяет одному или нескольким потокам ждать, пока другие потоки завершат свои задачи.Он инициализируется счётчиком, и каждый вызов countDown() уменьшает его на 1. Как только счётчик достигает нуля — все ожидающие потоки продолжают выполнение.
• Удобен для ожидания завершения группы потоков
• Позволяет синхронизировать старт или окончание работы
• Прост в использовании и потокобезопасен
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable worker = () -> {
System.out.println(Thread.currentThread().getName() + " завершил работу");
latch.countDown();
};
new Thread(worker, "Поток 1").start();
new Thread(worker, "Поток 2").start();
new Thread(worker, "Поток 3").start();
latch.await(); // ждём, пока счётчик не станет 0
System.out.println("Все потоки завершили работу");
}
}
👀 CountDownLatch особенно полезен для синхронизации в тестах, многопоточных вычислениях и в случаях, когда нужно дождаться инициализации нескольких компонентов перед стартом основной логики.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
👍3🔥1
Когда стоит использовать CopyOnWriteArrayList в Java?
Пример
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
for (String s : list) {
list.add("C"); // не вызовет ConcurrentModificationException
}
System.out.println(list);
}
}
Ответ
CopyOnWriteArrayList — это потокобезопасная реализация List, которая копирует весь массив при каждой модификации.
Она идеально подходит, когда:
— много операций чтения и мало операций записи
— нужно избежать ConcurrentModificationException при итерации
— важна простая потокобезопасность без внешней синхронизации
📌 Недостаток — высокая стоимость записи, так как создаётся новая копия массива при каждом изменении.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Please open Telegram to view this post
VIEW IN TELEGRAM
CyclicBarrier — это синхронизатор из пакета
java.util.concurrent, который позволяет группе потоков дождаться друг друга, прежде чем продолжить выполнение.В отличие от CountDownLatch, барьер можно использовать многократно — после того как все потоки достигли точки синхронизации, он сбрасывается и готов к новому циклу.
• Синхронизирует несколько потоков в «точках встречи»
• Можно выполнять дополнительное действие при достижении барьера
• Подходит для повторяющихся этапов работы в параллельных задачах
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Main {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("Все потоки достигли барьера, продолжаем!")
);
Runnable task = () -> {
try {
System.out.println(Thread.currentThread().getName() + " готов");
barrier.await();
System.out.println(Thread.currentThread().getName() + " продолжает работу");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
};
new Thread(task, "Поток 1").start();
new Thread(task, "Поток 2").start();
new Thread(task, "Поток 3").start();
}
}
👀 CyclicBarrier особенно полезен, когда несколько потоков должны синхронизировать свои шаги — например, в параллельных алгоритмах, симуляциях, или играх с одновременным обновлением состояния.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
В чём разница между ExecutorService и ForkJoinPool в Java?
Пример
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class Main {
public static void main(String[] args) {
// Пример с ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> System.out.println("Задача в ExecutorService"));
executor.shutdown();
// Пример с ForkJoinPool
ForkJoinPool pool = new ForkJoinPool();
int sum = pool.invoke(new SumTask(1, 5));
System.out.println("Сумма: " + sum);
}
static class SumTask extends RecursiveTask<Integer> {
int start, end;
SumTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= 1) {
return start + end;
}
int mid = (start + end) / 2;
SumTask left = new SumTask(start, mid);
SumTask right = new SumTask(mid + 1, end);
left.fork();
return right.compute() + left.join();
}
}
}
Ответ
ExecutorService — это абстракция для управления пулом потоков и выполнения задач (Runnable или Callable) в очереди. Он отлично подходит для стандартных многопоточных задач, где нужно управлять количеством потоков и временем жизни пула.
ForkJoinPool — это специальная реализация ExecutorService, оптимизированная для задач типа divide-and-conquer, где задачи рекурсивно делятся на подзадачи (через RecursiveTask или RecursiveAction) и выполняются с помощью алгоритма "work-stealing".
📌 Если задачи независимые и нет сложного деления — хватит обычного ExecutorService. Если нужна параллельная рекурсия и максимальная загрузка процессоров — лучше ForkJoinPool.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Phaser — это гибкий синхронизатор из пакета
java.util.concurrent, который работает как усовершенствованный вариант CyclicBarrier и CountDownLatch. Он позволяет потокам синхронизироваться по фазам (этапам) выполнения задачи.Главное отличие: количество участников (parties) можно динамически регистрировать и снимать с регистрации, что делает Phaser более универсальным.
• Поддерживает несколько фаз (циклов синхронизации)
• Позволяет динамически добавлять/удалять потоки
• Можно строить сложные сценарии синхронизации
import java.util.concurrent.Phaser;
public class Main {
public static void main(String[] args) {
Phaser phaser = new Phaser(3); // 3 потока-участника
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " — фаза 1");
phaser.arriveAndAwaitAdvance(); // ждём остальных
System.out.println(Thread.currentThread().getName() + " — фаза 2");
phaser.arriveAndAwaitAdvance(); // ждём остальных
};
new Thread(task, "Поток 1").start();
new Thread(task, "Поток 2").start();
new Thread(task, "Поток 3").start();
}
}
👀 Phaser особенно полезен, когда у задачи есть несколько этапов, и все потоки должны переходить к следующему этапу только синхронно — например, в симуляциях, распределённых вычислениях или пошаговых моделях.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2❤1
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое CompletableFuture в Java и чем он отличается от Future?
Пример
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(str -> str + ", world!")
.thenApply(String::toUpperCase);
System.out.println(future.get()); // ➔ HELLO, WORLD!
}
}
Ответ
Future позволяет запустить задачу в отдельном потоке и получить результат, но работать с ним неудобно: нет удобных методов для цепочек, обработки ошибок и асинхронных комбинаций.
CompletableFuture — это расширение Future, которое:
— поддерживает асинхронные цепочки вычислений (thenApply, thenAccept, thenCompose)
— умеет комбинировать несколько задач (allOf, anyOf)
— позволяет удобно обрабатывать исключения (exceptionally)
— работает в стиле промисов из JavaScript
📌 Future подходит для простых асинхронных вычислений, а CompletableFuture — для сложных асинхронных цепочек и реактивного программирования.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Semaphore — это синхронизатор из пакета java.util.concurrent, который ограничивает количество потоков, одновременно обращающихся к ресурсу. Он работает как счётчик разрешений (permits): поток должен получить разрешение, чтобы продолжить работу, и обязан его освободить после завершения.Это удобно, когда ресурс ограничен — например, доступ к базе, пулу соединений или ограниченному количеству файлов.
• Контролирует доступ к общим ресурсам
• Поддерживает справедливый режим (fair) для очередности
• Помогает избежать перегрузки системных ресурсов
import java.util.concurrent.Semaphore;
public class Main {
private static final Semaphore semaphore = new Semaphore(2); // максимум 2 потока одновременно
public static void main(String[] args) {
Runnable task = () -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " получил доступ");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " освобождает доступ");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
};
for (int i = 1; i <= 5; i++) {
new Thread(task, "Поток " + i).start();
}
}
}
👀 Semaphore особенно полезен в системах, где необходимо ограничить количество одновременных операций — например, при обращении к API с лимитами, управлении пулом соединений или ограничении нагрузки на сервис.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5