До сих пор мы создавали и управляли потоками вручную.
Однако, когда в приложении вам нужно создать множество задач, которые будут выполняться во множестве потоков, или нужно создать множество задач, которые будут выполняться по очереди в одном потоке, возникают проблемы.
Например, у вас есть веб-приложение, которое помимо прочей обработки пользовательского запроса, в фоновом потоке посылает email.
Потоки, используемые для обработки запросов, поступающих на сервер приложений, создаются и управляются самим контейнером сервера. Вам не нужно об этом беспокоиться.
Однако, если вашему приложению необходимо обрабатывать долгие задачи, инициированные пользовательскими запросами, и вы хотите быстро отвечать на запросы, вы можете использовать отдельные потоки для этих задач.
Самым простым решением, было бы создание нового потока Thread каждый раз, когда поступает запрос пользователя, и обслуживать этот запрос в новом потоке.
Однако при этом накладные расходы на создание нового потока для каждого запроса являются значительными.
Сервер, создавая новый поток для каждого запроса, будет тратить дополнительное время и будет потреблять дополнительные системные ресурсы, создавая и уничтожая потоки.
В дополнение к накладным расходам на создание и уничтожение потоков, активные потоки потребляют системные ресурсы.
Создание слишком большого количества потоков в одной JVM может привести к тому, что система исчерпает память из-за чрезмерного потребления памяти.
Чтобы предотвратить переполнение ресурсов, серверные приложения нуждаются в ограничении количества запросов, обрабатываемых в любой момент времени.
Решить все эти проблемы поможет программный интерфейс Java Concurrency API, который предлагает интерфейсы для запуска и управления жизненным циклом задач, для планирования выполнения задач, обеспечивая создание очереди задач и пула потоков.
Executor — это простой интерфейс, содержащий метод execute для запуска задачи, заданной объектом Runnable.
ExecutorService — расширяет интерфейс Executor, добавляя функции для управления жизненным циклом задач.
ExecutorService также предоставляет метод submit, который может принимать объект Runnable, а также объекты Callable, которые позволяют задаче вернуть значение.
ScheduledExecutorService — расширяет интерфейс ExecutorService, добавляя функциональность для планирования выполнения задач.
Помимо вышеуказанных трех интерфейсов, Concurrency API также предоставляет класс Executors, который содержит фабричные методы для создания различных видов ExecutorService.
Под капотом, при использовании Executor интерфейсов, задачи Runnable сначала помещаются в очередь, а затем их выполнение распределяется по рабочим потокам пула потоков Thread Pool.
Пул потоков состоит из рабочих потоков, которые существуют отдельно от выполняемых задач и могут использоваться для выполнения нескольких задач.
Существует несколько типов пулов потоков.
Это фиксированный пул потоков, который содержит определенное количество потоков.
И если поток каким-то образом завершается во время своего использования, он автоматически заменяется новым потоком.
Задачи отправляются в пул потоков через внутреннюю очередь, которая содержит задачи, если количество задач больше, чем размер пула потоков.
Далее есть кэшированный пул потоков, который создает новые потоки по мере необходимости, но будет использовать ранее созданные потоки, когда они будут доступны.
И есть пул с одним потоком и неограниченной очередью.
Если этот единственный поток завершается из-за сбоя во время выполнения, на его место создается новый поток.
Задачи выполняются последовательно, и только одна задача будет активна в любое время.
Классы ThreadPoolExecutor и ScheduledThreadPoolExecutor позволяют установить свои собственные настройки для объекта Executor и определить основной размер пула потоков, максимальный размер пула потоков, указать тип используемой очереди и другое.
При этом если пул потоков не достиг еще своего основного размера, он создает новые потоки.
Если основной размер достигнут и нет простаивающих потоков, задачи ставятся в очередь.
Если основной размер достигнут, нет простаивающих потоков, и очередь заполнена, создаются новые потоки (пока не будет достигнут максимальный размер).
Если достигнут максимальный размер, нет простаивающих потоков, и очередь заполнена, новые задачи отклоняются.
Таким образом, пул потоков предлагает решение проблемы с накладными расходами на создание и уничтожение потоков, и проблемы чрезмерного потребления ресурсов при создании слишком большого количества потоков.
Пул потоков создается единовременно и при повторном использовании потоков для нескольких задач, накладные расходы на создание потоков распределены по многим задачам.
Дополнительно, так как поток уже существует, когда создается задача, устраняется задержка, возникающая при создании потока, что делает приложение более отзывчивым.
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии