Почему Alibaba отключает Executors для создания пулов потоков?

Java задняя часть

В разделе параллельного программирования руководства по разработке Alibaba есть статья: Пулы потоков не могут использовать Executors для создания, но через ThreadPoolExecutor, причина его отключения с помощью анализа исходного кода.

написать впереди

Прежде всего, спасибо, что прочитали эту статью в промежутке между постройкой здания.Прочитав эту статью, вы узнаете:

  • Определение пула потоков
  • Несколько способов для исполнителей создавать пулы потоков
  • Объекты ThreadPoolExecutor
  • Связь между логикой задачи выполнения пула потоков и параметрами пула потоков
  • Исполнители создают возвращаемые объекты ThreadPoolExecutor
  • Тест исключения OOM
  • Как определить параметры пула потоков

Если вы просто хотите узнать причину, вы можете вытащить ее прямо в сводку.

Определение пула потоков

Управление набором рабочих потоков. Повторное использование потоков через пул потоков имеет следующие преимущества:

  • Уменьшите создание ресурсов => уменьшите накладные расходы на память, создайте потоки, занимающие память
  • Уменьшите нагрузку на систему => создание потока требует времени, задерживает обработку запросов
  • Улучшить стабильность => избежать создания бесконечного потокаOutOfMemoryError[сокращенно ООМ]

Как исполнители создают пулы потоков

Создание пулов потоков в зависимости от типа возвращаемых объектов можно разделить на три категории:

  • Создать и вернуть объект ThreadPoolExecutor
  • Создание и возврат объекта ScheduleThreadPoolExecutor
  • Создать и вернуть объект ForkJoinPool

В этой статье обсуждается только создание возвратовThreadPoolExecutorобъект

Объект ThreadPoolExecutor

ПредставляемExecutorsПрежде чем создавать метод пула потоков, давайте представим егоThreadPoolExecutor, так как все эти статические методы создания пулов потоков возвращаютThreadPoolExecutorобъект, и мы вручную создаемThreadPoolExecutorОтличие объектов в том, что нам не нужно самим передавать параметры конструктора.ThreadPoolExecutorЕсть четыре конструктора, но все они вызывают один и тот же:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

Описание параметра конструктора:

  • corePoolSize => количество основных потоков пула потоков
  • maxPoolSize => максимальное количество пулов потоков
  • keepAliveTime => время выживания бездействующего потока
  • единица => единица времени
  • workQueue => буферная очередь, используемая пулом потоков
  • threadFactory => фабрика, используемая пулом потоков для создания потоков
  • обработчик => стратегия обработки пула потоков для отклоненных задач

Связь между логикой задачи выполнения пула потоков и параметрами пула потоков

Описание логики выполнения:

  • Определите, заполнено ли количество основных потоков, размер количества основных потоков иcorePoolSizeПараметр связан, если он не полный, создайте поток для выполнения задачи
  • Если основной пул потоков заполнен, определите, заполнена ли очередь, а также заполнена ли очередь иworkQueueПараметр связан, если он не заполнен, он будет добавлен в очередь
  • Если очередь заполнена, определите, заполнен ли пул потоков, а также заполнен ли пул потоков иmaximumPoolSizeПараметр связан, если он не полный, создайте поток для выполнения задачи
  • Если пул потоков заполнен, политика отказа используется для обработки задач, которые невозможно выполнить.handlerсвязанный с параметром

Исполнители создают возвращаемые объекты ThreadPoolExecutor

ExecutorsСуществует три способа создания и возврата объекта ThreadPoolExecutor:

  • Executors#newCachedThreadPool => Создать кэшируемый пул потоков
  • Executors#newSingleThreadExecutor => Создать пул однопоточных потоков
  • Executors#newFixedThreadPool => Создать пул потоков фиксированной длины

Executors#newCachedThreadPool метод

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

CachedThreadPoolэто пул потоков, который создает новые потоки по мере необходимости

  • corePoolSize => 0, количество пулов основных потоков равно 0
  • maxPoolSize => Integer.MAX_VALUE, максимальное количество пулов потоков — Integer.MAX_VALUE, можно считать, что потоки можно создавать бесконечно
  • keepAliveTime => 60L
  • единица => секунды
  • workQueue => SynchronousQueue

Когда поставлена ​​задача,corePoolSizeдля 0, чтобы не создавать основные потоки,SynchronousQueueЭто очередь, которая не хранит элементы.Можно понять, что очередь всегда заполнена, поэтому в конечном итоге будут созданы неосновные потоки для выполнения задач. Для неосновных потоков они будут перезапущены при бездействии в течение 60 с.так какInteger.MAX_VALUEОчень большой, можно считать, что потоки можно создавать бесконечно, и легко вызвать исключения OOM в случае ограниченных ресурсов

Executors#newSingleThreadExecutor метод

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

SingleThreadExecutorэто однопоточный пул потоков только с одним основным потоком

  • corePoolSize => 1, количество пулов основных потоков равно 1
  • maxPoolSize => 1, максимальное количество пулов потоков равно 1, то есть можно создать не более одного потока, и единственный поток — основной поток
  • keepAliveTime => 0L
  • единица => миллисекунды
  • workQueue => LinkedBlockingQueue

При отправке задачи сначала создается основной поток для выполнения задачи. Если количество основных потоков превышает количество основных потоков, задача помещается в очередь.так какLinkedBlockingQueueдлинаInteger.MAX_VALUEОчередь можно рассматривать как неограниченную очередь, поэтому в очередь может быть вставлено бесконечное количество задач, что легко вызвать, когда ресурсы ограничены.OOMаномальный, а из-за неограниченной очередиmaximumPoolSizeиkeepAliveTimeПараметр будет недопустимым, и неосновной поток вообще не будет создан.

Executors#newFixedThreadPool метод

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

FixedThreadPoolЭто пул фиксированных основных потоков, и количество фиксированных основных потоков передается пользователем.

  • corePoolSize => nThreads, количество пулов основных потоков равно 1
  • maxPoolSize => nThreads, максимальное количество пулов потоков равно nThreads, то есть максимально могут быть созданы только потоки nThreads
  • keepAliveTime => 0L
  • единица => миллисекунды
  • рабочая очередь => LinkedBlockingQueue это иSingleThreadExecutorТочно так же единственная разница заключается в количестве основных потоков, и из-заиспользуетLinkedBlockingQueue, что легко вызвать, когда ресурсы ограниченыOOMаномальный

Суммировать:

  • FixedThreadPool и SingleThreadExecutor => Допустимая длина очереди запросов — Integer.MAX_VALUE, что может аккумулировать большое количество запросов, вызываяOOMаномальный
  • CachedThreadPool => Число потоков, разрешенных для создания, равно Integer.MAX_VALUE, что может создать большое количество потоков, вызываяOOMаномальный

Вот почему запрещено использоватьExecutorsДля создания пула потоков рекомендуется создать его самостоятельноThreadPoolExecutorпричина

Тест исключения OOM

Теоретически будетOOMИсключение, необходимо протестировать волну, чтобы проверить предыдущее утверждение: Класс теста: TaskTest.java

public class TaskTest {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        int i = 0;
        while (true) {
            es.submit(new Task(i++));
        }
    }
}

использоватьExecutorsсозданныйCachedThreadPool, Добавить потоки в пул потоков Перед началом тестового классаJVMРегулировка памяти должна быть меньше, иначе компьютер легко столкнется с проблемами [Не спрашивайте меня, почему я знаю, это Tie Hanhantian! ! ! 】,существуетideaвнутри:Run -> Edit Configurations

image.png
JVMОписание параметра:

  • -Xms10M => Java Heapзначение инициализации памяти
  • -Xmx10M => Java Heapмаксимальная память

результат операции:

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Disconnected from the target VM, address: '127.0.0.1:60416', transport: 'socket'

Начать отчет, когда создано более 3 недельных тем.OOMОшибка
Два других пула потоков тестироваться не будут, методы тестирования те же, но созданные пулы потоков разные.

Как определить параметры пула потоков

  • интенсивное использование процессора=> Рекомендуемый размер пула потоковCPUколичество + 1,CPUЧислоRuntime.availableProcessorsметод получить
  • Интенсивный ввод-вывод => CPUколичество *CPUИспользование * (1 + время ожидания потока / процессорное время потока)
  • Гибридный=> Разделите задачи наCPUинтенсивный иIOИнтенсивный, а затем использовать разные пулы потоков для обработки, чтобы каждый пул потоков можно было настроить в соответствии с его собственной рабочей нагрузкой.
  • очередь блокировки=> Рекомендуется использовать ограниченную очередь, что помогает избежать исчерпания ресурсов
  • политика отказа=> По умолчаниюAbortPolicyПолитика отказа, кинутая прямо в программуRejectedExecutionExceptionИсключение [потому что это исключение времени выполнения, а не обязательноеcatch], этот подход недостаточно элегантен. Существует несколько рекомендуемых стратегий обработки отказов:
    • захват в программеRejectedExecutionExceptionException, задача обрабатывается в пойманном исключении. против политики отказа по умолчанию
    • использоватьCallerRunsPolicyСтратегия отклонения, эта стратегия дает задание потоку, вызывающему выполнение, для выполнения [обычно основной поток], в это время основной поток не сможет отправлять какие-либо задачи в течение определенного периода времени, так что рабочий поток может обработать выполняемую задачу. Темы, отправленные в этот момент, будут сохранены вTCPВ очереди полная очередь TCP повлияет на клиента, что приведет к незначительному снижению производительности.
    • Пользовательская политика отказа, просто нужно реализоватьRejectedExecutionHandlerинтерфейс
    • Если задача не особо важна, используйтеDiscardPolicyиDiscardOldestPolicyПолитика отказа также может отбросить задачу.

Если вы используете статический метод Executors для созданияThreadPoolExecutorобъект, доступ к которому можно получить с помощьюSemaphoreОграничение выполнения задач также позволяет избежать возникновенияOOMаномальный
Из-за отсутствия опыта в определении параметров пула потоков, это все теоретические знания, опытные начальники могут добавить.

Учись понемногу каждый день, давай, добро пожаловать лайк


Прикреплен к предыдущим статьям: добро пожаловать, читайте, лайкайте и комментируйте

Связанные с шаблоном проектирования:
1. Паттерн синглтон, ты правда правильно пишешь?
2. (Стратегический режим + Заводской режим + карта) переключатель case в пакете Kill project

Связанные с JAVA8:
1. Оптимизируйте свой код с помощью Stream API
2. Уважаемый, вместо Date рекомендуется использовать LocalDateTime

Связанные с базой данных:
1. Сравнение эффективности запросов типов времени базы данных mysql datetime, bigint и timestamp.
2. Очень доволен! Наконец-то ступил на яму медленного запроса

- эффективный:
1. Создайте каркас Java, чтобы унифицировать стиль структуры командного проекта.

Связанный журнал:
1. Структура журнала, выберите Logback или Log4j2?
2. Файл конфигурации Logback пишется так, а TPS увеличен в 10 раз.

Связанные с инженерией:
1. Когда вам нечего делать, пишите LRU локальный кеш
2. Redis реализует аналогичный функциональный модуль
3. JMX Visual Monitoring Thread Pool
4. Управление разрешениями [Spring Security]
5. Spring пользовательская аннотация от входа до мастерства
6. Java имитирует посадку на Youku
7. QPS такой высокий, давайте писать многоуровневый кеш
8. Java использует phantomjs для создания снимков экрана

разное:
1. Используйте попытку с ресурсами, чтобы изящно закрыть ресурсы
2. Босс, зачем использовать сумму плавающего хранилища, чтобы вычесть мою оплату?