| Красиво, пожалуйста, лайкните, выработайте привычку
У тебя одна мысль, у меня одна мысль, после того как мы обменяемся, у одного человека две мысли
If you can NOT explain it simply, you do NOT understand it well enough
Теперь демо-код и технические статьи отсортированы вместе.Выбор практики Github, всем удобно читать и просматривать, эта статья тоже попала сюда, я думаю это хорошо, пожалуйста, тоже поставьте звезду🌟
предыдущий постВ интервью меня спросили, сколько потоков уместно создать? что я должен сказатьОт качественного до количественного анализа, как создать правильное количество потоков, чтобы максимально использовать системные ресурсы (фактически, несколько математических задач для начальной школы). Вообще говоря, с этими знаниями хорошо вручную создавать соответствующее количество потоков по мере необходимости.
Но на самом деле вы, возможно, слышали или вас спрашивали:
Старайтесь избегать создания потоков вручную и используйте пулы потоков для унифицированного управления потоками.
Почему такое требование? В чем причина этого? Следуя этой эмпирической теории, ручное создание потоков должно быть недостатком.
Каковы недостатки ручного создания потоков?
- неконтролируемый риск
- Частое создание дорого
неконтролируемый риск
Этот недостаток, я думаю, вы также можете сказать один или два
Системные ресурсы ограничены, и каждый может вручную создавать потоки для разных бизнесов, да и стандарты создания разные (например, у потоков нет имен). Когда система работает, все потоки лихорадочно хватают ресурсы, неорганизованные и недисциплинированные, и можно представить себе хаотичные сцены (если есть проблема, естественно, ее невозможно легко найти и решить)
Если есть волшебный друг, который создает поток на каждый запрос, когда поступает большое количество запросов, это как обычная троянская программа, память безжалостно сливается и исчерпывается (ты безжалостный, ты безжалостный, ты неразумный )
Кроме того, слишком много потоков, естественно, вызовут накладные расходы на переключение контекста.
В целом неконтролируемые риски высоки
Частое создание дорого
Вопросы для интервью:Что плохого в том, чтобы часто создавать темы вручную?
отвечать:высокая стоимость
Это кажется правильным ответом, на который можно ответить, не задумываясь. Тогда я буду продолжать спрашивать
Интервьюер:Каковы накладные расходы на создание потока? Чем это отличается от создания обычного объекта Java?
отвечать:... эм ... ах
Согласно общепринятому пониманию, new Thread() создает поток, а new Object() ничем не отличается. Все в Java связано с объектами, потому что предком Thread также является Object.
Если вы действительно понимаете это, это означает, что вы не очень хорошо понимаете жизненный цикл потоков, пожалуйста, оглянитесь на предыдущийЖизненный цикл потока Java очень прост для понимания
В этой статье мы разъясняем, что new Thread() не создает новый поток на уровне операционной системы, что характерно для языков программирования. Настоящее преобразование заключается в создании потока на уровне операционной системы, а также в вызове API ядра операционной системы, после чего операционная система выделяет ряд ресурсов для потока.
Без лишних слов давайте сравним их:
новая процедура Object()
Object obj = new Object();
Когда мне понадобится [объект], я дам себе новый (интересно, вы такой же, как я), этот процесс должен быть вам знаком:
- Выделить блок памяти M
- Инициализировать объект в памяти M
- Присвоить адрес памяти M ссылочной переменной obj
это так просто
Процесс создания треда
Как упоминалось выше, создание потока также вызывает API ядра операционной системы. Чтобы лучше понять накладные расходы на создание и запуск потока, нам нужно увидеть, что JVM делает для нас за кулисами:
- Он выделяет память для стека потока, который содержит кадр стека для каждого вызова метода потока.
- Каждый кадр стека состоит из массива локальных переменных, возвращаемых значений, стеков операндов и пулов констант.
- Некоторые jvms, поддерживающие собственные методы, также выделяют собственный стек.
- Каждый поток получает программный счетчик, который сообщает ему, какую инструкцию выполняет текущий процессор.
- Система создает собственный поток, соответствующий потоку Java.
- Добавьте дескрипторы, связанные с потоком, во внутренние структуры данных JVM.
- Потоки совместно используют области кучи и методов
Это описание немного абстрактно: сколько места занимает создание потока (даже если он ничего не делает) с данными? Ответ примерно1M
о
java -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -version
На картинке выше результат моего теста с Java8, 19 потоков, зарезервированный и отправленный около 19000+ КБ, средний размер каждого потока около 1М (результат Java11 совершенно другой, вы можете проверить это сами)
Я полагаю, вы уже поняли, что стоимость частого ручного создания/удаления потоков очень высока сейчас, когда требования к производительности строги, и решением, естественно, является известный вам пул потоков.
Что такое пул потоков?
Ваши общие пулы соединений с базой данных, пулы экземпляров, пулы XX, пулы OO и различные пулы — все это идеи объединения.Короче говоря, чтобы максимизировать выгоды и минимизировать риски, идея объединения ресурсов для управления
Java также предоставляет собственную реализацию модели пула потоков —ThreadPoolExecutor
. Применяя описанное выше представление о пулах, пул потоков Java должен максимизировать повышение производительности, обеспечиваемое высоким параллелизмом, минимизировать риск ручного создания потоков и унифицировать управление несколькими потоками вместе.
Чтобы понять эту идею управления, нам нужно только сосредоточиться наThreadPoolExecutor
Метод построения
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
Такой сложный метод строительства действительно редко встречается в JDK.Чтобы каждый мог более наглядно понять эти основные параметры, мы объясним Праздник Весны (Пекин-Шанхай), который испытал большинство людей.
серийный номер | имя параметра | Описание параметра | Описание изображения Праздник Весны |
---|---|---|---|
1 | corePoolSize | Указывает количество резидентных потоков ядра. Если оно больше 0, оно не будет уничтожено, даже если локальная задача будет выполнена. | Ежедневное фиксированное количество поездов (независимо от того, идет ли речь о путешествии на Праздник Весны, должно быть фиксированное количество этих поездов) |
2 | maximumPoolSize | Указывает, что пул потоков может вместить максимальное количество потоков, которые могут выполняться одновременно. | Из-за большого пассажиропотока во время праздника Весны добавляются временные поезда.После добавления поездов общее количество поездов не может превышать это максимальное значение, иначе будут проблемы, такие как сбой расписания (в сочетании с очередью на работу) |
3 | keepAliveTime | Указывает время простоя потоков в пуле потоков. Когда время простоя достигает этого значения, поток уничтожается, оставляя толькоcorePoolSize расположение потоков |
После поездки на Праздник Весны временно добавьте автомобиль (если время простоя превышаетkeepAliveTime ) будут удалены, и для ежедневных операций будет зарезервировано только фиксированное количество ежедневных поездов. |
4 | unit |
keepAliveTime Единица времени , в конечном итоге будет преобразована в [наносекунды], потому что скорость выполнения процессора чрезвычайно высока. |
keepAliveTime Единица Chunyun рассчитывается в [днях]. |
5 | workQueue | Когда количество запрошенных потоков превышаетcorePoolSize Когда поток входит в очередь блокировки |
Давление Праздника Весны чрезвычайно велико (достигаетcorePoolSize ) не может соответствовать требованиям, все заявки на поездку будут поставлены в очередь на блокировку, очередь заполнена, есть дополнительные заявки, нужно добавить машину |
6 | threadFactory | Как следует из названия, фабрика потоков используется для создания набора потоков одной и той же задачи, и ее также можно использовать для добавления префиксов имен, чтобы сделать анализ стека виртуальной машины более понятным. | Например, (Пекин-Шанхай) относится ко всем префиксам поезда, что указывает на ответственность за перевозку поездом. |
7 | handler | Применять политику отказа, когдаworkQueue достичь верхнего пределаmaximumPoolSize С этим надо бороться, например отбраковывать, отбрасывать и т.д. Это мера защиты для ограничения тока |
когдаworkQueue Очередь также достигает максимальной строки очереди,maximumPoolSize Необходимо подсказать стратегию отказа типа нет билета, потому что не можем добавить вагон, а все текущие поезда полностью загружены |
В целом это выглядит так:
Только представьте, если будет запрос, то будет построен новый поезд, а когда запрос закончится, состав будет «уничтожен», и частые возвратно-поступательные операции точно будут неприемлемы.
Видно, что использование пула потоков может не только завершить работу, которую можно выполнить, создав потоки вручную, но и заполнить пробелы, которые не могут выполнить ручные потоки. Подводя итог, роль пула потоков включает в себя:
- Используйте пул потоков для управления потоками и их использования, а также для управления максимальным числом параллелизма (ручно созданные потоки трудно гарантировать).
- Реализовать стратегию кэширования очереди потоков задач и механизм отклонения
- Реализовать некоторые функции, связанные с практикой, такие как выполнение по времени, периодическое выполнение и т. д. (например, движение поезда в указанное время)
- Например, в среде с изолированным потоком служба транзакций и служба поиска находятся на одном сервере, соответственно открыты два пула потоков, и потребление ресурсов потоком транзакций, очевидно, больше. Таким образом, путем настройки независимого пула потоков более медленная служба транзакций и служба поиска разделены, чтобы избежать взаимодействия между потоками службы.
Я полагаю, что к этому моменту вы уже поняли основную идею пула потоков.В процессе использования необходимо объяснить несколько мер предосторожности.
Идеи/меры предосторожности при использовании пула потоков
Политика отклонения пула потоков, которую нельзя игнорировать
Нам сложно точно предсказать максимальный параллелизм в будущем, поэтому важным шагом является настройка разумной стратегии отклонения. По умолчанию ThreadPoolExecutor предоставляет четыре стратегии отклонения:
-
AbortPolicy: политика отклонения по умолчанию, которая выдает исключение RejectedExecutionException для отклонения
-
CallerRunsPolicy: поток, отправляющий задачу, выполняет саму задачу.
-
DiscardOldestPolicy: отбрасывание самой старой задачи фактически отбрасывает задачу, которая первой вошла в рабочую очередь, а затем добавляет новую задачу в рабочую очередь.
-
DiscardPolicy: довольно смелая стратегия, напрямую отбрасывающая задачи без каких-либо исключений.
Различные фреймворки (Netty, Dubbo) имеют разные стратегии отказа, мы также можем реализоватьRejectedExecutionHandler
Пользовательская политика отказа
Какую стратегию использовать, зависит от важности выполняемой задачи. Если это какая-то неважная задача, вы можете отказаться от нее напрямую; если это важная задача, вы можете использовать более раннюю версию (так называемая пониженная версия — это меры по исправлению положения, предпринимаемые, когда служба не может нормально выполнять функции. Конкретное понижение означает также зависит от конкретных сценариев) обработка, например вставка информации о задаче в базу данных или очередь сообщений, а также включение пула потоков, предназначенного для компенсации, чтобы компенсировать
Не существует абсолютной стратегии отказа, есть только подходящая для него, но не игнорируйте стратегию отказа в процессе проектирования.
Отключить использование Executors для создания пулов потоков
Я полагаю, что многие люди видели эту проблему (в Руководстве по разработке Java для Alibaba указано, что запрещено использовать Executors для создания пулов потоков), я помещаю исходный скриншот (P247) здесь:
Исполнители значительно упрощают создание различных типов пулов потоков, так почему бы их не использовать?
На самом деле, пока вы открываете его и смотрите на параметры его статического метода, вы поймете
Входящая workQueue является границейInteger.MAX_VALUE
Очередь, мы также можем назвать это замаскированной неограниченной очередью, потому что граница слишком велика, такая большая очередь ожидания также очень потребляет память
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
Кроме того, в методе ThreadPoolExecutor используется политика отклонения по умолчанию (прямой отказ), но не все бизнес-сценарии подходят для использования этой политики: когда приходит очень важный запрос, явно нецелесообразно напрямую выбирать его отклонение.
В общем, пул потоков, созданный Executors, слишком идеален и не может соответствовать многим реальным бизнес-сценариям, поэтому мы обязаны создать его через ThreadPoolExecutor и передать соответствующие параметры
Суммировать
Когда нам нужно часто создавать потоки, нам нужно рассмотреть возможность унифицированного управления ресурсами потоков через пул потоков, чтобы избежать неконтролируемых рисков и дополнительных накладных расходов.
После понимания нескольких концепций основных параметров пула потоков нам также необходимо пройти процесс настройки, чтобы установить наилучшие значения параметров потока (этот процесс необходим)
Хотя пул потоков восполняет дефекты и пробелы ручного создания потоков, в то же время разумная стратегия понижения может значительно повысить стабильность системы.
Руководства Alibaba являются сутью бесчисленных резюме предшественников после заполнения ям.Вы также должны следовать соответствующим инструкциям и устанавливать соответствующие параметры для создания пула потоков на основе ваших реальных бизнес-сценариев.
вопрос души
- Мы сказали так много хорошего о пулах потоков, каковы недостатки или ограничения использования пулов потоков?
- Почему не рекомендуется, чтобы все предприятия совместно использовали пул потоков? Каковы недостатки?
- Какими способами можно указать префикс для параметров пула потоков?
Ссылаться на
Спасибо старшим за изложение сути, многие параллельно написанные мной серии относятся к следующим материалам
- Параллельное программирование на Java на практике
- Красота параллельного программирования на Java
- Эффективный код
- Искусство параллельного программирования на Java
- ifeve
- Техническая команда Meituan
Личный блог: https://dayarch.top
добавь меня в друзья, Войдите в группу для развлечения, обучения и обмена и отметьте «войти в группу».
Добро пожаловать, чтобы продолжать обращать внимание на общественный номер: «Сун Гун И Бин».
- Передовая технология Java для обмена галантереей
- Резюме эффективных инструментов | Ответ на «Инструменты»
- Анализ вопроса интервью и ответ
- Сбор технических данных | Ответ на «данные»
Узнайте о стеке технологий Java легко и весело, думая о чтении детективных романов, и постепенно разлагайте технические проблемы, основываясь на принципах упрощения сложных проблем, конкретизации абстрактных проблем и графики.Технология постоянно обновляется, пожалуйста, продолжайте платить внимание...