Ответ 1
После исследований по различным многопоточным фреймворкам за последние 3 месяца я нашел ответ на вопрос.
Он прост и удобен в использовании с ограниченным контролем. Вы можете использовать его
- Чтобы запустить параллельные независимые задачи без ожидания
- Подождите завершения всех ваших задач.
Я предпочитаю этот вариант, когда число задач Callable/Runnable
невелико, а нагромождение задач в неограниченной очереди не вызывает накапливания в памяти и ухудшает производительность системы.
Он скрывает детали низкого уровня ThreadPoolExecutor
. Он не позволяет играть с другими параметрами (Bounded Queue, Rejection Handler
и т.д. Для точной настройки производительности), как в ThreadPoolExectuor
.
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Он предоставляет вам больше контроля. Помимо установки минимального и максимального потоков, вы можете установить размер очереди и сделать BlockingQueue
ограниченным.
Вы можете создать свой собственный поток factory, если вам нужны ниже функции
- Чтобы задать более описательное имя потока
- Чтобы установить статус демона нити
- Чтобы установить приоритет потока
Если ваше приложение ограничено количеством ожидающих задач Runnable/Callable, вы будете использовать ограниченную очередь, установив максимальную емкость. Когда очередь достигает максимальной емкости, вы можете определить RejectionHandler. Java предоставляет четыре типа обработчиков отклонения .
-
В стандартном
ThreadPoolExecutor.AbortPolicy
обработчик выдает исключение ExjectExceptionException после отклонения. -
В
ThreadPoolExecutor.CallerRunsPolicy
поток, который вызывает сам выполнение, запускает задачу. Это обеспечивает простой механизм управления обратной связью, который замедляет скорость отправки новых задач. -
В
ThreadPoolExecutor.DiscardPolicy
задача, которая не может быть выполнена, просто удаляется. -
В
ThreadPoolExecutor.DiscardOldestPolicy
, если исполнитель не закрыт, задача во главе рабочей очереди отбрасывается, а затем выполняется повторное выполнение (что может снова потерпеть неудачу, в результате чего это будет повторяться.)
CountDownLatch
: эта структура позволяет потоку java ждать, пока другой набор потоков завершит выполнение своих задач.
Варианты использования:
-
Достижение максимума Parallelism: Иногда мы хотим запустить несколько потоков одновременно для достижения максимального parallelism
-
Подождите, пока N потоков не завершится до начала выполнения другого блока кода
-
Обнаружение взаимоблокировки.
Более подробная информация приведена в статье
ForkJoinPool
похож на Java ExecutorService, но с одним отличием. ForkJoinPool
облегчает задачу разбиения их работы на более мелкие задачи, которые затем отправляются в ForkJoinPool. Устранение задачи происходит в ForkJoinPool, когда свободные рабочие потоки захватывают задачи из очереди занятых рабочих потоков.
public ForkJoinPool(int parallelism,
ForkJoinPool.ForkJoinWorkerThreadFactory factory,
Thread.UncaughtExceptionHandler handler,
boolean asyncMode)
Creates a ForkJoinPool with the given parameters.
Параметры:
parallelism - уровень parallelism. Для значения по умолчанию используйте Runtime.availableProcessors()
.
factory - factory для создания новых потоков. Для значения по умолчанию используйте defaultForkJoinWorkerThreadFactory.
обработчик - обработчик для внутренних рабочих потоков, которые завершаются из-за неустранимых ошибок
asyncMode - если true, устанавливает локальный режим планирования первого в первом порядке для разветвленных задач, которые никогда не соединяются.
Относительно основного запроса:
Вы можете использовать рамки ExecutorService.invokeAll()
или CountDownLatch
или ForkJoinPool
. Все эти рамки дополняют друг друга с различной степенью детализации для управления выполнением задач с высокого уровня на низкий уровень.
EDIT:
Посмотрите на связанные вопросы SE:
В чем преимущества использования ExecutorService?
Java-приложение Fork/Join vs ExecutorService - когда использовать, который?