предисловие
Только лысина может стать сильнее
Оглядываясь назад на фронт:
- ThreadLocal так просто
- Вы можете войти в дверь за три минуты с многопоточностью!
- Базовые знания многопоточности! После прочтения изучение многопоточности может сделать больше с меньшими затратами
- Механизм блокировки Java для понимания
- AQS просто пройти
- Заблокируйте подкласс, чтобы понять
Эта статья предназначена в основном для объяснения пула потоков, это моя предпоследняя статья о многопоточности.Позже будет тупик. В основном будет многопоточнымпроходить через,послеИметь возможность идти дальше!
Тогда давайте начнем.Если в статье есть ошибки, пожалуйста, потерпите меня и поправьте меня в комментариях~
Отказ от ответственности: в этой статье используется JDK1.8.
1. Введение в пул потоков
Пул потоков можно рассматривать какколлекция потоков. Когда задачи нет, поток находится в состоянии ожидания. Когда приходит запрос: пул потоков назначает незанятый поток запросу. После завершения задачи он возвращается в пул потоков для ожидания следующей задачи** (вместо того, чтобы уничтожить его). так чтоРеализовано повторное использование потоков**.
ПосмотримЕсли пул потоков не используетсяСитуация такова:
- Создавать новый поток для каждого запроса!
public class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
// 为每个请求都创建一个新的线程
final Socket connection = socket.accept();
Runnable task = () -> handleRequest(connection);
new Thread(task).start();
}
}
private static void handleRequest(Socket connection) {
// request-handling logic here
}
}
открывайте новый поток для каждого запроса, хотятеоретически возможно, Но тамнедостаток:
- Накладные расходы на время жизни потока очень высоки. Каждый поток имеет свой жизненный цикл,Создавать и уничтожать темыЭто может занять больше времени и ресурсов, чем обработка задачи клиента, и будут некоторыеБездействующий поток будет потреблять ресурсы.
- Стабильность и надежность программы снизятся, и каждый запрос будет открывать поток. Если происходит злонамеренная атака или слишком много запросов (недостаточно памяти), программа может легко выйти из строя.
Итак: нашПотоки лучше всего управляются пулом потоков, что может упростить управление жизненным циклом потока и в определенной степени повысить производительность.
Во-вторых, API пула потоков, предоставляемый JDK.
JDK дает намПлатформа исполнителяиспользовать пул потоков, этоОсновы пулов потоков.
- Executor предоставляет механизм для отделения «отправки задачи» от «выполнения задачи» (разделение).
Давайте взглянем на общую архитектуру API пула потоков JDK:
Далее давайте взглянем на эти API:
Интерфейс исполнителя:
Интерфейс ExecutorService:
Класс AbstractExecutorService:
Интерфейс ScheduledExecutorService:
Класс ThreadPoolExecutor:
Класс ScheduledThreadPoolExecutor:
2.1 Пул потоков ForkJoinPool
В дополнение к пулу потоков классов ScheduledThreadPoolExecutor и ThreadPoolExecutor существуетJDK1.7 новыйПул потоков: пул потоков ForkJoinPool.
Таким образом, наша диаграмма классов может стать более полной:
Новый пул потоков в JDK1.7, такой как ThreadPoolExecutor, также наследует AbstractExecutorService. ForkJoinPool — это один из двух основных классов инфраструктуры Fork/Join. По сравнению с другими типами ExecutorService,Основное отличие заключается в использовании алгоритма work-stealing.: все пулы попытаются выполнить задачи, отправленные в пул или другими потоками. Это редко имеет простаивающий поток, очень эффективный. Это позволяет эффективно обрабатывать следующие сценарии:Большинство случаев, когда задача порождает большое количество подзадач; Тот случай, когда в пул поступает большое количество мелких задач от внешних клиентов.
источник:
2.2 Дополнение: Callable и Future
Изучив пул потоков, мы легко обнаружим, что многие API имеют две вещи, называемые Callable и Future.
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
На самом деле, это не очень глубокие вещи.
Мы легко можем подумать:Callable является расширением Runnable..
- Runnable не имеет возвращаемого значения и не может генерировать проверенные исключения, в то время как Callable может!
То есть: когда нашЗадаче требуется возвращаемое значение, мы можем использовать Callable!
Future обычно считается возвращаемым значением Callable, но на самом деле оно представляетжизненный цикл задачи(Конечно, он может получить возвращаемое значение Callable)
Просто посмотрите на их использование:
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 结束
pool.shutdown();
}
}
Вызываемая задача:
public class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}
Выполнив задание, вы сможетеПолучить данные, возвращенные задачей:
3. Подробное объяснение ThreadPoolExecutor
Это наиболее часто используемый пул потоков, поэтому данная статья будет посвящена ему.
Давайте посмотрим на верхнюю ноту:
3.1 Внутреннее состояние
Переменная ctl определена как AtomicInteger,Записываются две части информации: «количество задач в пуле потоков» и «статус пула потоков»..
Статус темы:
- ВЫПОЛНЯЕТСЯ: пул потоковУмеет принимать новые задачии обработайте только что добавленную задачу.
- ВЫКЛЮЧЕНИЕ: пул потоковНе могу принять новые задачиОднако вы можете обработать дополнительную задачу.
- СТОП: пул потоковНе получает новые задачи, не обрабатывает добавленные задачи и прерывает выполнение задач.
- УБОРКА: КогдаВсе задачи прекращены, «количество задач», записанное ctl, равно 0, и пул потоков перейдет в состояние TIDYING. Когда пул потоков перейдет в состояние TIDYING, будет выполнена функция-ловушка terminated(). в классе ThreadPoolExecutor пусто. Если пользователь хочет выполнить соответствующую обработку, когда пул потоков становится TIDYING, это может быть достигнуто путем перегрузки функции terminated().
- TERMINATED: пул потоковсостояние полного завершения.
Переход между состояниями:
3.2 Пулы, реализованные по умолчанию
Ниже я приведу три сравненияобщийПул реализации:
- newFixedThreadPool
- newCachedThreadPool
- SingleThreadExecutor
Если вы понимаете соответствующую стратегию выше и количество потоков, это не должно быть слишком сложно понять.
3.2.1newFixedThreadPool
Пул потоков с фиксированным числом потоков, который будет возвращатьПул потоков с равным corePoolSize и maxPoolSize.
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.2.2newCachedThreadPool
Очень эластичный пул нитей, для новой задачи, если в это время в пуле потоков нет простаивающих потоков,Пул потоков, не колеблясь, создаст новый поток для выполнения этой задачи..
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.2.3SingleThreadExecutor
Исполнитель, использующий один рабочий поток
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3.3 Метод строительства
После того, как мы прочитали приведенный выше пул реализации по умолчанию и соответствующие свойства, давайте вернемся к методу построения, чтобы увидеть
- Конструктор позволяет намПользовательский (расширенный) пул потоков
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- Укажите количество основных потоков
- Укажите максимальное количество потоков
- Разрешить время простоя потока
- объект времени
- очередь блокировки
- фабрика нитей
- политика отклонения задач
Подводя итог основным пунктам этих параметров еще раз:
Очки подсчета потоков:
- Если количество запущенных потоковменьше, чемколичество основных потоков, затемсоздать новоеПоток обрабатывает запрос
- Если количество запущенных потоковбольше, чемколичество основных потоков,меньше, чеммаксимальное количество потоков, то когдаСоздать новый, когда очередь заполненаразгром
- Если количество основных потоковравныймаксимальное количество потоков, тоСоздать фиксированный размерпул соединений
- Если установлено максимальное количество потоковбесконечный, затем позвольте пулу потоков соответствоватьлюбойколичество одновременных
Точки времени простоя потока:
- Текущее количество потоковбольше, чемКоличество основных потоков, если время простоя превышено, поток будетразрушать.
Очередь стратегических точек:
- Синхронная передача:не будет введен в очередь, но покажет тему, чтобы выполнить его.如果当前线程没有执行,很可能会Недавно открытыйПоток выполняется.
- Безлимитная стратегия:Если основной поток работает, поток будет помещен в очередь. Таким образом, количество потоков не будет превышать количество основных потоков.
- Ограниченная стратегия:Может избежать истощения ресурсов, но пропускная способность несколько снижается
Когда корпус закрыт или количество потоков заполнено нить, а очередь насыщена, существует отказ миссии:
Политика запрета задач:
- бросать исключение напрямую
- Использовать поток вызывающего абонента для обработки
- просто брось это задание
- удалить самую старую задачу
Четыре, выполнить метод выполнения
Метод выполнения execute разбит на три шага, которые прописаны в коде в виде комментариев~
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果线程池中运行的线程数量<corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果线程池中运行的线程数量>=corePoolSize,且线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,就再次检查线程池的状态,
// 1.如果线程池不是RUNNING状态,且成功从阻塞队列中删除任务,则该任务由当前 RejectedExecutionHandler 处理。
// 2.否则如果线程池中运行的线程数量为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果以上两种case不成立,即没能将任务成功放入阻塞队列中,且addWoker新建线程失败,则该任务由当前 RejectedExecutionHandler 处理。
else if (!addWorker(command, false))
reject(command);
}
В-пятых, пул потоков закрыт.
ThreadPoolExecutor предоставляетshutdown()
иshutdownNow()
Два способа закрыть пул потоков
неисправность() :
shutdownNow():
разница:
- После вызова shutdown() состояние пула потоков немедленноСтановится закрытым, при вызове shutdownNow() состояние пула потоковНемедленно меняется на STOP.
- shutdown()дождитесь завершения задачиТолько прервать поток и shutdownNow()Не ждите завершения задачиПрерванная нить.
6. Резюме
Этот пост в блоге в основном кратко представляет систему многопоточной структуры и рассказывает о том, как используется наиболее часто используемый пул потоков ThreadPoolExecutor~~~
Я надеюсь написать тупик завтра, так что следите за обновлениями~~~
Есть несколько оставшихся пулов потоков (приведены ссылки):
- ScheduledThreadPoolExecutor
- ForkJoinPool
Использованная литература:
- "Основная технология Java, том 1"
- «Практика параллельного программирования на Java»
- Блог CMS на.com/?Afraid_ID=11…
- blog.CSDN.net/Пан Вэйвэй 19…
- zhuanlan.zhihu.com/p/35382932
Проект с открытым исходным кодом, охватывающий все точки знаний о бэкэнде Java (уже 6 тысяч звезд):GitHub.com/Zhongf UC очень…
если ты хочешьв реальном времениЕсли вы обратите внимание на мои обновленные статьи и галантерейные товары, которыми я делюсь, поищите в WeChat.Java3y.
Содержимое PDF-документоввсе вручную, если вы ничего не понимаете, вы можете напрямуюспросите меня(В официальном аккаунте есть мои контактные данные).