предисловие
Tomcat/Jetty в настоящее время является самым популярным веб-контейнером.После того, как оба принимают запрос, они будут переданы в пул потоков для обработки, что может эффективно повысить производительность обработки и параллелизм. JDK улучшает реализацию полного пула потоков, но ни Tomcat, ни Jetty не используют его напрямую. Jetty принимает решение собственной разработки и внедряет его внутри компанииQueuedThreadPool
Компонент пула потоков, в то время как Tomcat использует схему расширения, опираясь на пул потоков JDK и расширяя собственный пул потоков JDK.
Можно сказать, что собственный пул потоков JDK относительно полнофункционален и относительно прост в использовании.Так почему же Tomcat/Jetty не выбрали это решение, а реализовали его самостоятельно?
Пул потоков JDK
Обычно мы можем разделить выполняемые задачи на две категории:
- ресурсоемкие задачи
- io интенсивные задачи
Задачи, интенсивно использующие ЦП, требуют сложных операций, выполняемых потоками в течение длительного времени. Этот тип задач требует создания меньшего количества потоков. Слишком большое количество потоков часто приводит к вышеуказанному переключению и снижает скорость обработки задачи.
Для задач с интенсивным вводом-выводом, поскольку потоки не выполняются все время, они могут тратить большую часть своего времени на ожидание ввода-вывода для чтения/записи данных.Увеличение количества потоков может улучшить параллелизм и обработать как можно больше задач.
Рабочий процесс собственного пула потоков JDK выглядит следующим образом:
Подробности можно посмотретьВ этой статье рассказывается, как безопасно закрыть пул потоков., приведенный выше рисунок предполагает использование
LinkedBlockingQueue
.
Душевная пытка: вы пропустили описанный выше процесс? Долгое время я думал, что количество потоков достигло максимального количества потоков перед помещением в очередь.  ̄□ ̄||
Как видно на приведенном выше рисунке, пока количество потоков в пуле потоков больше, чем количество основных потоков, задача будет добавлена в очередь задач первой, а новый поток будет создан только в том случае, если очередь задач не может присоединиться. Другими словами, до того, как очередь пула собственных потоков будет заполнена, останется не более чем количество основных потоков.
Эта стратегия, очевидно, больше подходит для работы сcpu
интенсивные задачи, но дляio
Интенсивные задачи, такие как запросы к базе данных, вызовы запросов rpc и т. д., не очень дружелюбны.
Поскольку Tomcat/Jetty необходимо обрабатывать большое количество задач клиентских запросов, если используется собственный пул потоков, как только количество принятых запросов превышает количество основных потоков в пуле потоков, эти запросы будут помещены в очередь. и дождитесь обработки основного потока. Очевидно, что это снижает общую скорость обработки этих запросов, поэтому ни один из них не использует собственный пул потоков JDK.
Чтобы решить приведенное выше решение, вы можете реализовать компонент пула потоков, такой как сам Jetty, который может лучше адаптироваться к внутренней логике, но разработка сложнее, Другой, как Tomcat, расширяет собственный пул потоков JDK и относительно прост. реализовать.
Далее следует в основном расширение пула потоков с помощью Tomcat и обсуждение принципа его реализации.
Расширить пул потоков
Во-первых, мы начнем с исходного кода пула потоков JDK, чтобы увидеть, как расширяться на этой основе.
Видно, что процесс пула потоков в основном делится на три этапа, второй этап основан наqueue#offer
Метод возвращает результат, чтобы определить, нужно ли создавать новый поток.
Собственный тип очереди JDKLinkedBlockingQueue
, SynchronousQueue
, логика реализации у них разная.
LinkedBlockingQueue
offer
Внутри метод будет судить, заполнена ли очередь или нет. Если очередь заполнена, вернутьсяfalse
, если очередь не заполнена, добавить задачу в очередь и вернутьсяtrue
.
SynchronousQueue
Эта очередь является специальной и не хранит никаких данных внутри. Если поток помещает в него задачу, он будет заблокирован до тех пор, пока другой поток не возьмет задачу. И наоборот, если никакие другие потоки не помещают в нее задачи, метод взятия задач из очереди также будет заблокирован до тех пор, пока другие потоки не поставят в нее задачи.
дляoffer
метод, если другие потоки блокируются методом выборки, метод вернетtrue
. В противном случае метод предложения вернет false.
Поэтому, если вы хотите реализовать пул потоков, подходящий для задач с интенсивным вводом-выводом, то есть отдать приоритет новым потокам для обработки задач, ключ лежит вqueue#offer
метод. Внутреннюю логику этого метода можно переписать, пока текущее количество пулов потоков меньше максимального количества потоков, метод возвращаетfalse
, пул потоков создает новый поток для обработки.
Конечно, приведенная выше логика реализации довольно грубая, давайте проверим логику ее реализации из исходного кода Tomcat.
Пул потоков расширения Tomcat
Пул потоков расширения Tomcat напрямую наследует пул потоков JDKjava.util.concurrent.ThreadPoolExecutor
, чтобы переопределить логику некоторых методов. Также реализованоTaskQueue
, прямое наследованиеLinkedBlockingQueue
, переписатьoffer
метод.
Сначала посмотрите на использование пула потоков Tomcat.
Вы можете видеть, что метод использования пула потоков Tomcat мало чем отличается от обычного пула потоков.
Далее давайте рассмотрим основные методы пула потоков Tomcat.execute
логика.
execute
Логика метода относительно проста, и ядро задачи по-прежнему передается для обработки собственному пулу потоков Java. Здесь мы в основном добавляем стратегию повторных попыток.Если собственный пул потоков выполняет стратегию отказа, бросаетRejectedExecutionException
аномальный. Это зафиксирует, а затем снова попытается добавить задачу вTaskQueue
, выполнить задание как можно лучше.
Обратите внимание здесьsubmittedCount
Переменная. Это важный параметр в пуле потоков Tomcat.AtomicInteger
переменная, которая будет подсчитывать в реальном времени задачи, отправленные в пул потоков, но еще не выполненные. то естьsubmittedCount
Равняется количеству задач в очереди пула потоков плюс задачи, выполняемые рабочими потоками пула потоков.TaskQueue#offer
С помощью этого параметра будет реализована соответствующая логика.
Тогда мы в основном рассматриваемTaskQueue#offer
логика метода.
Основная логика заключается в третьем шаге, здесь, еслиsubmittedCount
Меньше, чем количество потоков в текущем пуле потоков, будет возвращеноfalse
. Выше мы упоминалиoffer
метод возвращаетfalse
, пул потоков будет создавать новые потоки напрямую.
Добавлена версия Dubbo 2.6.XEagerThreadPool
, принцип его реализации аналогичен пулу потоков Tomcat, и заинтересованные партнеры могут его прочитать самостоятельно.
компромиссный подход
Хотя описанный выше метод расширения не кажется сложным, затраты на его самостоятельную реализацию могут быть относительно большими. Если вы не хотите расширять пул потоков для выполнения задач с интенсивным вводом-выводом, вы можете использовать следующий компромисс.
new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(100));
Однако использование этого метода сделаетkeepAliveTime
Сбой, после создания потока он всегда будет существовать, что является пустой тратой системных ресурсов.
Суммировать
JDK относительно хорошо реализует функции пула потоков, но он больше подходит для выполнения задач с интенсивным использованием ЦП, а не задач с интенсивным вводом-выводом. Для задач с интенсивным вводом-выводом это можно сделать косвенно, установив параметры пула потоков.
Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: программа для общения, ежедневный толчок галантерейных товаров. Если вас интересует мой рекомендуемый контент, вы также можете подписаться на мой блог:studyidea.cn