阿里大牛珍藏架构资料,点击链接免费获取
существует"Углубленный анализ исходного кода принципа реализации пула потоков Java«В этой статье мы представили общее использование и основные принципы пулов потоков в Java.
В тексте есть описание:
Пулы потоков можно создавать с помощью статической фабрики Executors, но обычно это не рекомендуется.
Подробной разработки по этому вопросу в той статье нет. Причина, по которой автор говорит это, заключается в том, что этот способ создания пула потоков имеет большие скрытые опасности, и небольшая небрежность может привести к сбоям в сети, таким как: кровавый случай и сводка, вызванные неправильным использованием пула потоков Java (zhuanlan.zhihu.com/p/32867181)
В этой статье мы сосредоточимся на этой проблеме, чтобы проанализировать, почему метод построения пулов потоков, предоставляемый самим JDK, не рекомендуется? Как именно должен быть создан пул потоков?
Executors
Executors — это служебный класс в Java. Предоставляет фабричные методы для создания различных типов пулов потоков.

Из приведенного выше рисунка также видно, что метод Executors для создания пула потоков, созданный пул потоков реализует интерфейс ExecutorService. Общие методы следующие:
newFiexedThreadPool(int Threads)
: создать пул потоков с фиксированным числом потоков.
newCachedThreadPool()
: создает кэшируемый пул потоков, и вызов execute будет повторно использовать ранее созданные потоки (если потоки доступны). Если поток недоступен, новый поток создается и добавляется в пул. Завершить и удалить из кеша те потоки, которые не использовались в течение 60 секунд.
newSingleThreadExecutor()
Создайте однопоточный Executor.
newScheduledThreadPool(int corePoolSize)
Создайте пул потоков, поддерживающий запланированное и периодическое выполнение задач, который в большинстве случаев можно использовать вместо класса Timer.
Класс кажется относительно мощным, и он использует фабричный шаблон и имеет относительно сильную расширяемость.Важно то, что он более удобен в использовании, например:
ExecutorService executor = Executors.newFixedThreadPool(nThreads) ;
Вы можете создать пул потоков фиксированного размера.
Но почему я говорю, что не рекомендуется использовать этот класс для создания пула потоков?
Я упомянул «не рекомендуется», но это также четко указано в руководстве по разработке Alibaba Java, и используемое слово «не разрешено» использовать Executors для создания пулов потоков.

Что не так с Исполнителями
В Руководстве по разработке Java для Alibaba упоминается, что использование Executors для создания пула потоков может вызвать OOM (OutOfMemory, переполнение памяти), но это не объясняет почему, поэтому давайте посмотрим, почему Executors не разрешены?
Давайте начнем с простого примера, чтобы имитировать использование Executors для вызова OOM.
/**
* @author Hollis
*/
public class ExecutorsDemo {
private static ExecutorService executor = Executors.newFixedThreadPool(15);
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(new SubThread());
}
}
}
class SubThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
}
}
Указав аргументы JVM:-Xmx8m -Xms8m
Запуск приведенного выше кода вызовет OOM:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
В приведенном выше коде указано, чтоExecutorsDemo.java
16-я строка кода — этоexecutor.execute(new SubThread());
.
Почему исполнители несовершенны
Из приведенного выше примера мы знаем, чтоExecutors
Созданный пул потоков имеет риск OOM, так в чем же причина? нам нужно углубитьсяExecutors
для анализа исходного кода.
Фактически, в приведенном выше сообщении об ошибке мы можем видеть подсказки.На самом деле, в приведенном выше коде было сказано, что настоящая причина OOM на самом делеLinkedBlockingQueue.offer
метод.
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
Если читатели взглянут на код, они также могут обнаружить, что нижний слой действительно сквозной.LinkedBlockingQueue
Реализовано:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
Если читатель что-то знает о блокировке очередей в Java, возможно, вы сможете понять причину этого.
на ЯвеBlockingQueue
В основном есть две реализации, а именноArrayBlockingQueue
а такжеLinkedBlockingQueue
.
ArrayBlockingQueue
представляет собой ограниченную блокирующую очередь, реализованную с помощью массива, и ее емкость должна быть установлена.
LinkedBlockingQueue
Это ограниченная очередь блокировки, реализованная со связанным списком. Емкость может быть установлена опционально. Если она не установлена, это будет неограниченная очередь блокировки. Максимальная длинаInteger.MAX_VALUE
.
Проблема вот в чем:Если не задано, это будет неограниченная очередь блокировки с максимальной длиной Integer.MAX_VALUE.То есть, если мы не установимLinkedBlockingQueue
емкость, ее емкость по умолчанию будетInteger.MAX_VALUE
.
а такжеnewFixedThreadPool
создан вLinkedBlockingQueue
, емкость не указана. В настоящее время,LinkedBlockingQueue
Это неограниченная очередь. Для неограниченной очереди задачи могут добавляться в очередь непрерывно. В этом случае может возникнуть проблема переполнения памяти из-за слишком большого количества задач.
Перечисленные выше проблемы в основном находят свое отражение вnewFixedThreadPool
а такжеnewSingleThreadExecutor
На двух заводских методах не тоnewCachedThreadPool
а такжеnewScheduledThreadPool
Эти два метода безопасны, и максимальное количество потоков, созданных этими двумя методами, может бытьInteger.MAX_VALUE
, и создание такого количества потоков неизбежно приведет к OOM.
Правильная поза для создания пула потоков
Избегайте использования Executors для создания пула потоков, в основном, чтобы избежать использования реализации по умолчанию, тогда мы можем вызвать его напрямую.ThreadPoolExecutor
Конструктор для создания самого пула потоков. При создании дайтеBlockQueue
Просто укажите мощность.
private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
В этом случае, как только количество отправленных потоков превысит количество доступных в данный момент потоков, он выдастjava.util.concurrent.RejectedExecutionException
, это связано с тем, что очередь, используемая текущим пулом потоков, является ограниченной очередью, и очередь не может продолжать обрабатывать новые запросы, когда она заполнена. Но исключение лучше, чем ошибка.
Помимо определенияThreadPoolExecutor
за пределами. Есть и другие способы. В настоящее время вы должны впервые подумать о библиотеках классов с открытым исходным кодом, таких как apache и guava.
Автор рекомендует использовать ThreadFactoryBuilder, предоставляемый guava, для создания пула потоков.
public class ExecutorsDemo {
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
pool.execute(new SubThread());
}
}
}
При создании потока описанным выше способом можно не только избежать проблемы OOM, но и настроить имя потока, что упрощает отслеживание источника ошибки.
Вопрос мысли, автор сказал в статье: Лучше иметь Исключение, чем Ошибку, почему вы так говорите?
«Руководство по разработке Java для Alibaba», упомянутое в статье, обратите внимание на общедоступный аккаунт Холлис, ответ: руководство. Доступна полная версия PDF.