[英]Naming threads and thread-pools of ExecutorService
Let's say I have an application that utilizes the Executor
framework as such假设我有一个使用Executor
框架的应用程序
Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}
When I run this application in the debugger, a thread is created with the following (default) name: Thread[pool-1-thread-1]
.当我在调试器中运行此应用程序时,会使用以下(默认)名称创建一个线程: Thread[pool-1-thread-1]
。 As you can see, this isn't terribly useful and as far as I can tell, the Executor
framework does not provide an easy way to name the created threads or thread-pools.如您所见,这并不是非常有用,据我所知, Executor
框架没有提供一种简单的方法来命名创建的线程或线程池。
So, how does one go about providing names for the threads/thread-pools?那么,go 如何为线程/线程池提供名称? For instance, Thread[FooPool-FooThread]
.例如, Thread[FooPool-FooThread]
。
You could supply a ThreadFactory
to newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
.您可以向newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
提供一个ThreadFactory
。 The factory will be responsibe for creating threads, and will be able to name them.工厂将负责创建线程,并能够命名它们。
To quote the Javadoc :引用Javadoc :
Creating new threads创建新线程
New threads are created using a
ThreadFactory
.新线程是使用ThreadFactory
创建的。 If not otherwise specified, aExecutors.defaultThreadFactory()
is used, that creates threads to all be in the sameThreadGroup
and with the sameNORM_PRIORITY
priority and non-daemon status.如果没有另外指定,则使用 Executors.defaultThreadFactoryExecutors.defaultThreadFactory()
,它创建的线程都在同一个ThreadGroup
中,并且具有相同的NORM_PRIORITY
优先级和非守护进程状态。 By supplying a differentThreadFactory
, you can alter the thread's name, thread group, priority, daemon status, etc. If aThreadFactory
fails to create a thread when asked by returning null fromnewThread
, the executor will continue, but might not be able to execute any tasks通过提供不同的ThreadFactory
,您可以更改线程的名称、线程组、优先级、守护程序状态等。如果ThreadFactory
在从newThread
返回 null 时未能创建线程,则执行程序将继续,但可能无法执行任何任务
You can try to provide your own thread factory, which will create thread with appropriate names.您可以尝试提供自己的线程工厂,它将创建具有适当名称的线程。 Here's one example:这是一个例子:
class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, "Your name");
}
}
Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);
You can also change the name of your thread afterwards, while the thread is executed:您还可以在执行线程时更改线程的名称:
Thread.currentThread().setName("FooName");
That could be of interest if for instance you're using the same ThreadFactory for different type of tasks.例如,如果您将相同的 ThreadFactory 用于不同类型的任务,这可能会很有趣。
The BasicThreadFactory
from apache commons-lang is also useful to provide the naming behavior. BasicThreadFactory
commons-lang 中的 BasicThreadFactory 也可用于提供命名行为。 Instead of writing an anonymous inner class, you can use the Builder to name the threads as you want.无需编写匿名内部 class,您可以使用 Builder 来根据需要命名线程。 Here's the example from the javadocs:这是来自 javadocs 的示例:
// Create a factory that produces daemon threads with a naming pattern and
// a priority
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("workerthread-%d")
.daemon(true)
.priority(Thread.MAX_PRIORITY)
.build();
// Create an executor service for single-threaded execution
ExecutorService exec = Executors.newSingleThreadExecutor(factory);
If you are using Spring, there is CustomizableThreadFactory
for which you can set a thread name prefix.如果您使用的是 Spring,则可以使用CustomizableThreadFactory
为其设置线程名称前缀。
Example:例子:
ExecutorService alphaExecutor =
Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));
Alternatively, you can create your ExecutorService
as a Spring bean using ThreadPoolExecutorFactoryBean
- then the threads will all be named with the beanName-
prefix.或者,您可以使用ThreadPoolExecutorFactoryBean
将ExecutorService
创建为 Spring bean - 然后所有线程都将使用beanName-
前缀命名。
@Bean
public ThreadPoolExecutorFactoryBean myExecutor() {
ThreadPoolExecutorFactoryBean executorFactoryBean = new ThreadPoolExecutorFactoryBean();
// configuration of your choice
return executorFactoryBean;
}
In the example above, the threads will be named with myExecutor-
prefix.在上面的示例中,线程将以myExecutor-
前缀命名。 You can set the prefix explicitly to a different value (eg. "myPool-"
) by setting executorFactoryBean.setThreadNamePrefix("myPool-")
on the factory bean.您可以通过在工厂 bean 上设置executorFactoryBean.setThreadNamePrefix("myPool-")
将前缀显式设置为不同的值(例如"myPool-"
)。
There's an open RFE for this with Oracle. Oracle 对此有一个开放的 RFE 。 From the comments from the Oracle employee it seems they don't understand the issue and won't fix.从 Oracle 员工的评论来看,他们似乎不理解这个问题,也不会解决。 It's one of these things that is dead simple to support in the JDK (without breaking backwards compatibility) so it is kind of a shame that the RFE gets misunderstood.这是 JDK 中支持非常简单的事情之一(不会破坏向后兼容性),因此 RFE 被误解是一种耻辱。
As pointed out you need to implement your own ThreadFactory .正如所指出的,您需要实现自己的ThreadFactory 。 If you don't want to pull in Guava or Apache Commons just for this purpose I provide here a ThreadFactory
implementation that you can use.如果你不想仅仅为了这个目的而引入 Guava 或 Apache Commons,我在这里提供了一个你可以使用的ThreadFactory
实现。 It is exactly similar to what you get from the JDK except for the ability to set the thread name prefix to something else than "pool".除了可以将线程名称前缀设置为“池”以外的其他内容之外,它与您从 JDK 获得的内容完全相同。
package org.demo.concurrency;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* ThreadFactory with the ability to set the thread name prefix.
* This class is exactly similar to
* {@link java.util.concurrent.Executors#defaultThreadFactory()}
* from JDK8, except for the thread naming feature.
*
* <p>
* The factory creates threads that have names on the form
* <i>prefix-N-thread-M</i>, where <i>prefix</i>
* is a string provided in the constructor, <i>N</i> is the sequence number of
* this factory, and <i>M</i> is the sequence number of the thread created
* by this factory.
*/
public class ThreadFactoryWithNamePrefix implements ThreadFactory {
// Note: The source code for this class was based entirely on
// Executors.DefaultThreadFactory class from the JDK8 source.
// The only change made is the ability to configure the thread
// name prefix.
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
/**
* Creates a new ThreadFactory where threads are created with a name prefix
* of <code>prefix</code>.
*
* @param prefix Thread name prefix. Never use a value of "pool" as in that
* case you might as well have used
* {@link java.util.concurrent.Executors#defaultThreadFactory()}.
*/
public ThreadFactoryWithNamePrefix(String prefix) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup()
: Thread.currentThread().getThreadGroup();
namePrefix = prefix + "-"
+ poolNumber.getAndIncrement()
+ "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
When you want to use it you simply take advantage of the fact that all Executors
methods allow you to provide your own ThreadFactory
.当您想使用它时,您只需利用所有Executors
方法都允许您提供自己的ThreadFactory
的事实。
This这个
Executors.newSingleThreadExecutor();
will give an ExecutorService where threads are named pool-N-thread-M
but by using将给出一个 ExecutorService ,其中线程被命名为pool-N-thread-M
但使用
Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc"));
you'll get an ExecutorService where threads are named primecalc-N-thread-M
.你会得到一个 ExecutorService ,其中线程被命名为primecalc-N-thread-M
。 Voila!瞧!
As other answers already said, you may create and use your own implementation of the java.util.concurrent.ThreadFactory
interface (no external libraries required).正如其他答案已经说过的那样,您可以创建和使用自己的java.util.concurrent.ThreadFactory
接口实现(不需要外部库)。 I am pasting my code below because it is different than previous answers since it uses String.format
method and takes a base name for the threads as a constructor argument:我在下面粘贴我的代码,因为它与以前的答案不同,因为它使用String.format
方法并将线程的基本名称作为构造函数参数:
import java.util.concurrent.ThreadFactory;
public class NameableThreadFactory implements ThreadFactory{
private int threadsNum;
private final String namePattern;
public NameableThreadFactory(String baseName){
namePattern = baseName + "-%d";
}
@Override
public Thread newThread(Runnable runnable){
threadsNum++;
return new Thread(runnable, String.format(namePattern, threadsNum));
}
}
And this is an example of usage:这是一个使用示例:
ThreadFactory threadFactory = new NameableThreadFactory("listenerThread");
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);
EDIT : making my ThreadFactory
implementation thread-safe, thanks to @mchernyakov for pointing it out.编辑:使我的ThreadFactory
实现线程安全,感谢@mchernyakov指出。
Even though nowhere in the ThreadFactory
documentation is said that its implementations must be thread-safe, the fact that the DefaultThreadFactory
is thread-safe is a big hint:尽管ThreadFactory
文档中没有任何地方说它的实现必须是线程安全的,但DefaultThreadFactory
是线程安全的事实是一个很大的提示:
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class NameableThreadFactory implements ThreadFactory{
private final AtomicInteger threadsNum = new AtomicInteger();
private final String namePattern;
public NameableThreadFactory(String baseName){
namePattern = baseName + "-%d";
}
@Override
public Thread newThread(Runnable runnable){
return new Thread(runnable, String.format(namePattern, threadsNum.addAndGet(1)));
}
}
A quick and dirty way is to use Thread.currentThread().setName(myName);
一种快速而肮脏的方法是使用Thread.currentThread().setName(myName);
in the run()
method.在run()
方法中。
private class TaskThreadFactory implements ThreadFactory
{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "TASK_EXECUTION_THREAD");
return t;
}
}
Pass the ThreadFactory to an executorservice and you are good to go将 ThreadFactory 传递给 executorservice,你就可以使用 go
Extend ThreadFactory扩展线程工厂
public interface ThreadFactory
An object that creates new threads on demand.按需创建新线程的 object。 Using thread factories removes hardwiring of calls to new Thread, enabling applications to use special thread subclasses, priorities, etc.使用线程工厂消除了对新线程调用的硬连线,使应用程序能够使用特殊的线程子类、优先级等。
Thread newThread(Runnable r)
Constructs a new Thread.构造一个新线程。 Implementations may also initialize priority, name, daemon status, ThreadGroup, etc.实现还可以初始化优先级、名称、守护进程状态、线程组等。
Sample code:示例代码:
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
class SimpleThreadFactory implements ThreadFactory {
String name;
AtomicInteger threadNo = new AtomicInteger(0);
public SimpleThreadFactory (String name){
this.name = name;
}
public Thread newThread(Runnable r) {
String threadName = name+":"+threadNo.incrementAndGet();
System.out.println("threadName:"+threadName);
return new Thread(r,threadName );
}
public static void main(String args[]){
SimpleThreadFactory factory = new SimpleThreadFactory("Factory Thread");
ThreadPoolExecutor executor= new ThreadPoolExecutor(1,1,60,
TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());
final ExecutorService executorService = Executors.newFixedThreadPool(5,factory);
for ( int i=0; i < 100; i++){
executorService.submit(new Runnable(){
public void run(){
System.out.println("Thread Name in Runnable:"+Thread.currentThread().getName());
}
});
}
executorService.shutdown();
}
}
output: output:
java SimpleThreadFactory
thread no:1
thread no:2
Thread Name in Runnable:Factory Thread:1
Thread Name in Runnable:Factory Thread:2
thread no:3
thread no:4
Thread Name in Runnable:Factory Thread:3
Thread Name in Runnable:Factory Thread:4
thread no:5
Thread Name in Runnable:Factory Thread:5
....etc ....ETC
I find it easiest to use a lambda as a thread factory if you just want to change the name for a single thread executor.如果您只想更改单线程执行器的名称,我发现使用 lambda 作为线程工厂最简单。
Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
The home-grown core Java solution that I use to decorate existing factories:我用来装修现有工厂的国产核心Java方案:
public class ThreadFactoryNameDecorator implements ThreadFactory {
private final ThreadFactory defaultThreadFactory;
private final String suffix;
public ThreadFactoryNameDecorator(String suffix) {
this(Executors.defaultThreadFactory(), suffix);
}
public ThreadFactoryNameDecorator(ThreadFactory threadFactory, String suffix) {
this.defaultThreadFactory = threadFactory;
this.suffix = suffix;
}
@Override
public Thread newThread(Runnable task) {
Thread thread = defaultThreadFactory.newThread(task);
thread.setName(thread.getName() + "-" + suffix);
return thread;
}
}
In action:在行动:
Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());
Runnable getJob() {
return () -> {
// your job
};
}
I use to do same like below (requires guava
library):我曾经像下面那样做同样的事情(需要guava
库):
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);
Based on few of the comments above, difference is I just used lambda基于上面的一些评论,不同的是我只使用了 lambda
Executors.newFixedThreadPool(10, r -> new Thread(r, "my-threads-%d"))
You can write your own implementation of ThreadFactory, using for example some existing implementation (like defaultThreadFactory) and change the name at the end.您可以编写自己的 ThreadFactory 实现,例如使用一些现有实现(如 defaultThreadFactory)并在最后更改名称。
Example of implementing ThreadFactory:实现 ThreadFactory 的示例:
class ThreadFactoryWithCustomName implements ThreadFactory {
private final ThreadFactory threadFactory;
private final String name;
public ThreadFactoryWithCustomName(final ThreadFactory threadFactory, final String name) {
this.threadFactory = threadFactory;
this.name = name;
}
@Override
public Thread newThread(final Runnable r) {
final Thread thread = threadFactory.newThread(r);
thread.setName(name);
return thread;
}
}
And usage:和用法:
Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
Executors.defaultThreadFactory(),
"customName")
);
This is my customized factory providing a customized names for thread dump analyzers.这是我的定制工厂,为线程转储分析器提供定制名称。 Usually I just give tf=null
to reuse JVM default thread factory.通常我只是给tf=null
来重用 JVM 默认线程工厂。 This website has more advanced thread factory. 这个网站有更先进的线程工厂。
public class SimpleThreadFactory implements ThreadFactory {
private ThreadFactory tf;
private String nameSuffix;
public SimpleThreadFactory (ThreadFactory tf, String nameSuffix) {
this.tf = tf!=null ? tf : Executors.defaultThreadFactory();
this.nameSuffix = nameSuffix;
}
@Override public Thread newThread(Runnable task) {
// default "pool-1-thread-1" to "pool-1-thread-1-myapp-MagicTask"
Thread thread=tf.newThread(task);
thread.setName(thread.getName()+"-"+nameSuffix);
return thread;
}
}
- - - - -
ExecutorService es = Executors.newFixedThreadPool(4, new SimpleThreadFactory(null, "myapp-MagicTask") );
For your convenience this is a thread dump loop for debug purpose.为了您的方便,这是一个用于调试目的的线程转储循环。
ThreadMXBean mxBean=ManagementFactory.getThreadMXBean();
long[] tids = mxBean.getAllThreadIds();
System.out.println("------------");
System.out.println("ThreadCount="+tids.length);
for(long tid : tids) {
ThreadInfo mxInfo=mxBean.getThreadInfo(tid);
if (mxInfo==null) {
System.out.printf("%d %s\n", tid, "Thread not found");
} else {
System.out.printf("%d %s, state=%s, suspended=%d, lockowner=%d %s\n"
, mxInfo.getThreadId(), mxInfo.getThreadName()
, mxInfo.getThreadState().toString()
, mxInfo.isSuspended()?1:0
, mxInfo.getLockOwnerId(), mxInfo.getLockOwnerName()
);
}
}
Using the existing functionality of Executors.defaultThreadFactory() but just setting the name:使用Executors.defaultThreadFactory()的现有功能,但只需设置名称:
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class NamingThreadFactory implements ThreadFactory {
private final String prefix;
private final AtomicInteger threadNumber = new AtomicInteger(0);
public NamingThreadFactory(String prefix){
this.prefix = prefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setName(prefix + threadNumber.addAndGet(1));
return t;
}
}
Thought I'd throw in some simplified examples, just so the options are all out there:想我会抛出一些简化的例子,所以选项都在那里:
Unique number (could also put this into a method):唯一编号(也可以将其放入方法中):
AtomicInteger threadNum = new AtomicInteger(0);
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "my-name-" + threadNum.incrementAndGet()));
Unique number and "probably" unique name (if you're generating new Runnable objects).唯一编号和“可能”唯一名称(如果您正在生成新的 Runnable 对象)。 Useful if starting off the threads is within a method that gets called more than once, for instance:如果启动线程是在一个被多次调用的方法中,例如:
AtomicInteger threadNum = new AtomicInteger(0);
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "my-name-" + threadNum.incrementAndGet() + "-" + r.hashCode()));
If you really wanted a unique name each time you'd need a class with a static var (and could also add a static pool number prefix in there as well, see other answers).如果您真的想要一个唯一的名称,每次您需要一个 class 和一个 static var(也可以在其中添加一个 static 池编号前缀)。
and an equivalent in JDK < 8 (you don't need a new class for it, or could return a ThreadFactory out of a method):以及 JDK < 8 中的等效项(您不需要新的 class ,或者可以从方法中返回 ThreadFactory ):
Executors.newSingleThreadExecutor(new ThreadFactory() {
AtomicInteger threadCount = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "your-name-" + threadCount.getAndIncrement() + "-" + r.hashCode()); // could also use Integer.toHexString(r.hashCode()) for shorter
}
}));
And could make that into a method for the "you-name-" aspect as a variable.并且可以将其作为“you-name-”方面作为变量的方法。 Or use a separate class with a constructor like the other answers all seem to.或者使用一个单独的 class 和一个构造函数,就像其他答案一样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.