Именование потоков и пулов в ExecutorService: решение
Быстрый ответ
Для присваивания имён потокам в ExecutorService
используйте пользовательскую ThreadFactory
. Вот пример фабрики, которая создаёт потоки с оригинальными именами:
ThreadFactory namedFactory = new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(0);
public Thread newThread(Runnable r) {
return new Thread(r, "PoolThread-" + threadNumber.incrementAndGet());
}
};
ExecutorService executor = Executors.newFixedThreadPool(5, namedFactory);
Таким образом, потоки будут обладать уникальными именами в виде "PoolThread-1", "PoolThread-2" и так далее, что обеспечит удобство их идентификации при выполнении работы.
Использование Guava и Apache
ThreadFactoryBuilder (подарочек от Guava)
Guava предоставляет изящный и простой инструмент – ThreadFactoryBuilder
:
ThreadFactory namedFactory = new ThreadFactoryBuilder()
.setNameFormat("Worker-%d")
.build();
ExecutorService service = Executors.newFixedThreadPool(10, namedFactory);
Присваивание имен потокам значительно облегчает процесс отладки и мониторинга, превращая поиск ошибок из утомительного занятия в приятный процесс.
BasicThreadFactory из комплекта Apache Commons-Lang
Среди инструментов Apache доступен BasicThreadFactory
:
ThreadFactory customThreadFactory = new BasicThreadFactory.Builder()
.namingPattern("CustomPool-%d")
.daemon(true)
.priority(Thread.MAX_PRIORITY)
.build();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(customThreadFactory);
Использование Apache позволяет с лёгкостью настроить потоки, управляя такими параметрами как принадлежность к демонам и приоритет.
Kotlin и лямбда-выражения
В Kotlin можно использовать ThreadFactory
и лямбда-выражения для определения потоков:
val executor = Executors.newFixedThreadPool(5) { runnable ->
Thread(runnable).apply {
name = "KotlinWorker-${someRandomIdGenerator()}"
}
}
Визуализация
Присваивание имен потокам можно сравнить с процессом присвоения имен солдатам в армии:
Пул потоков: [`Worker1`, `Worker2`, `Worker3`]
Чтобы сделать процесс нагляднее, приведём пример именования потоков:
ExecutorService execService = Executors.newFixedThreadPool(3, new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "WorkerBee-" + threadNumber.getAndIncrement());
}
});
В логах это будет выглядеть приближенно следующим образом:
[WorkerBee-1] выполняется работа...
[WorkerBee-2] продолжается активная работа...
[WorkerBee-3] "А мы должны работать? Я думал, это время для кофе-паузы."
Такой индивидуальный подход при именовании делает процесс мониторинга активности потоков более простым.
Подробности именования потоков
Присваивание имён для отдельных и запланированных потоков
Даже отдельные потоки могут быть индивидуализированы:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat("Worker-%d-on-a-mission").build()
);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("Scheduler-%d-running-against-clock").build()
);
Такого рода настройка помогает легко навигироваться при работе с различными видами исполнителей.
Многоразовые ThreadFactories для поддержания порядка
Создайте повторно используемую ThreadFactory
:
public class NamedThreadFactory implements ThreadFactory {
private final String baseName;
private final AtomicInteger threadNum = new AtomicInteger(1);
public NamedThreadFactory(String baseName) {
this.baseName = baseName;
}
@Override
public Thread newThread(Runnable r) {
return new Thread(r, baseName + "-" + threadNum.getAndIncrement());
}
}
// Пример использования:
ExecutorService pool = Executors.newCachedThreadPool(new NamedThreadFactory("SuperWorkers"));
В каких случаях эффективно использовать индивидуализацию имён потоков?
Практика персонализации имён оказывается эффективной в следующих случаях:
- Идентификации потоков, у которых высокое потребление процессорного времени
- Решении проблем с дедлоками и утечками памяти
- Организации работы нескольких пулов в сложных системах
Имена потоков должны быть уникальными, информативными и распознаваемыми, следовать общепринятому стандарту именования.
Полезные материалы
- Executors (Java Platform SE 8) — особенно полезные материалы JavaDoc, детально описывающие поведение стандартной фабрики потоков.
- Lesson: Concurrency (The Java™ Tutorials > Essential Java Classes) — отличный ресурс для глубокого понимания концепций многопоточности в Java.
- java.util.concurrent (Java Platform SE 7 ) — документация всех инструментов пакета concurrency в Java.
- [[JavaSpecialists 056] – Shutting down Threads Cleanly](https://www.javaspecialists.eu/archive/Issue056-Shutting-down-Threads-Cleanly.html) — статья о том, как правильно завершить работу потоков.
- ExecutorService (Java Platform SE 8 ) — исчерпывающее руководство по управлению ExecutorService.
- Java concurrency (multi-threading) – Tutorial — подробный учебник по многопоточности Java от Vogella.
- Effective Java, 3rd Edition — весомое издание, которое всем рекомендуется к прочтению с целью улучшения навыков в Java.