Ответ
Опыт охватывает низкоуровневые и высокоуровневые API многопоточности:
- Создание потоков:
Thread,Runnable,Callable. - Управление пулами:
ExecutorService,ForkJoinPool. - Асинхронные вычисления:
Future,CompletableFuture. - Синхронизация:
synchronized,ReentrantLock,ReadWriteLock,Semaphore. - Потокобезопасные коллекции:
ConcurrentHashMap,CopyOnWriteArrayList,BlockingQueue. - Атомарные операции:
volatile,AtomicIntegerи другие классы изjava.util.concurrent.atomic.
Ключевые решаемые проблемы: Race condition, deadlock, livelock, starvation.
Пример использования ExecutorService:
// Создание пула из 4 потоков
ExecutorService executor = Executors.newFixedThreadPool(4);
List<Future<Integer>> futures = new ArrayList<>();
// Отправка задач на выполнение
for (int i = 0; i < 10; i++) {
futures.add(executor.submit(() -> {
// Имитация работы задачи
return ThreadLocalRandom.current().nextInt(100);
}));
}
// Получение результатов
for (Future<Integer> f : futures) {
try {
System.out.println("Результат: " + f.get()); // get() блокирует поток
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// Обязательное завершение работы пула
executor.shutdown();
Почему ExecutorService предпочтительнее new Thread()? Эффективное управление ресурсами (переиспользование потоков), контроль над количеством одновременно выполняемых задач.
Дополнительно: Опыт с CompletableFuture для построения асинхронных цепочек и комбинирования результатов, а также с ForkJoinPool для рекурсивного параллелизма (например, обход деревьев).