命名ExecutorService的线程和线程池

假设我有一个利用Executor框架的应用程序

Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}

当我在调试器中运行此应用程序时,将创建一个具有以下(默认)名称的线程:Thread[pool-1-thread-1]。正如你所看到的,这并不是非常有用,而且据我所知,Executor框架并没有提供一种简单的方法来命名创建的线程或线程池。

那么,如何为线程/线程池提供名称呢?例如,Thread[FooPool-FooThread]

174324 次浏览

您可以尝试提供自己的线程工厂,它将创建具有适当名称的线程。这里有一个例子:

class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, "Your name");
}
}


Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);

或者用Kotlin

Executors.newSingleThreadExecutor { r -> Thread(r, "Your name") }

你可以提供一个ThreadFactorynewSingleThreadScheduledExecutor(ThreadFactory threadFactory)。工厂将负责创建线程,并能够为它们命名。

引用Javadoc:

创建新线程

使用ThreadFactory创建新线程。如果没有特别指定,则使用Executors.defaultThreadFactory(),它创建的所有线程都在相同的ThreadGroup中,具有相同的NORM_PRIORITY优先级和非守护进程状态。通过提供不同的ThreadFactory,你可以改变线程的名称、线程组、优先级、守护进程状态等。如果ThreadFactory在从newThread返回null时未能创建线程,则执行器将继续执行,但可能无法执行任何任务

apache common -lang中的BasicThreadFactory对于提供命名行为也很有用。您可以使用Builder根据需要命名线程,而不是编写匿名的内部类。下面是来自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);

番石榴几乎总是有你需要

ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()

并将它传递给你的ExecutorService

你也可以在线程执行之后更改线程的名称:

Thread.currentThread().setName("FooName");

例如,如果您在不同类型的任务中使用相同的ThreadFactory,这可能会很有趣。

private class TaskThreadFactory implements ThreadFactory
{


@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "TASK_EXECUTION_THREAD");


return t;
}


}

将ThreadFactory传递给一个executorservice,就可以开始了

一种快速而肮脏的方法是在run()方法中使用Thread.currentThread().setName(myName);

在Oracle中有一个打开RFE。从甲骨文员工的评论来看,他们似乎不理解这个问题,也不会解决。在JDK中支持它是非常简单的事情之一(不会破坏向后兼容性),所以RFE被误解有点遗憾。

如前所述,你需要实现你自己的ThreadFactory。如果你不想为了这个目的而引入Guava或Apache Commons,我在这里提供了一个你可以使用的ThreadFactory实现。除了能够将线程名前缀设置为“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;
}
}

当你想使用它时,你只需利用所有Executors方法都允许你提供自己的ThreadFactory这一事实。

    Executors.newSingleThreadExecutor();

将给出一个ExecutorService,其中线程命名为pool-N-thread-M,但使用

    Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc"));

你会得到一个ExecutorService,其中线程名为primecalc-N-thread-M。瞧!

如果使用Spring,可以为CustomizableThreadFactory设置线程名前缀。

例子:

ExecutorService alphaExecutor =
Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));

或者,你也可以使用ThreadPoolExecutorFactoryBean创建你的ExecutorService作为Spring bean——然后线程都将以beanName-前缀命名。

@Bean
public ThreadPoolExecutorFactoryBean myExecutor() {
ThreadPoolExecutorFactoryBean executorFactoryBean = new ThreadPoolExecutorFactoryBean();
// configuration of your choice
return executorFactoryBean;
}

在上面的例子中,线程将以myExecutor-前缀命名。您可以显式地将前缀设置为不同的值(例如。通过在工厂bean上设置executorFactoryBean.setThreadNamePrefix("myPool-")来实现。

延长ThreadFactory

< em > public interface ThreadFactory < / em >

根据需要创建新线程的对象。使用线程工厂消除了对新线程的硬连接,使应用程序能够使用特殊的线程子类、优先级等。

< em > Thread newThread(Runnable r) < / em >

构造一个新的线程。实现也可以初始化优先级、名称、守护进程状态、线程组等。

示例代码:

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();
}
}

输出:

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

Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());


Runnable getJob() {
return () -> {
// your job
};
}

您可以编写自己的ThreadFactory实现,例如使用一些现有的实现(如defaultThreadFactory),并在最后更改名称。

实现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;
}
}

和用法:

Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
Executors.defaultThreadFactory(),
"customName")
);

这是我的定制工厂,为线程转储分析器提供定制名称。通常我只是给tf=null来重用JVM默认线程工厂。本网站有比较先进的螺纹工厂。

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") );

为了方便起见,这是一个用于调试的线程转储循环。

    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()
);
}
}

我使用下面这样做(需要guava库):

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);

我用来装饰现有工厂的自制核心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;
}
}

在行动:

Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));

我发现,如果您只是想更改单个线程执行器的名称,那么使用lambda作为线程工厂是最简单的。

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
正如其他答案已经说过的,你可以创建并使用你自己的java.util.concurrent.ThreadFactory接口实现(不需要外部库)。 我将我的代码粘贴在下面,因为它与前面的答案不同,因为它使用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));
}
}

这是一个用法的例子:

ThreadFactory  threadFactory = new NameableThreadFactory("listenerThread");
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);

编辑:使我的ThreadFactory实现线程安全,感谢@mchernyakov指出它 尽管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)));
}
}

根据上面的一些注释,不同之处在于我只使用了lambda

Executors.newFixedThreadPool(10, r -> new Thread(r, "my-threads-%d"))

使用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;
}
}

我想我要抛出一些简单的例子,只是为了让选项都在那里:

唯一的数字(也可以把它放到一个方法中):

AtomicInteger threadNum = new AtomicInteger(0);
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "my-name-" + threadNum.incrementAndGet()));

唯一编号和“可能”;唯一名称(如果您正在生成新的Runnable对象)。如果启动线程是在一个被多次调用的方法中,例如:

AtomicInteger threadNum = new AtomicInteger(0);
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "my-name-" + threadNum.incrementAndGet() + "-" + r.hashCode()));

如果你真的想每次都有一个唯一的名字,你就需要一个带有静态变量的类(也可以在那里添加一个静态池号前缀,参见其他答案)。

和JDK <中的等效程序;8(你不需要一个新的类,或者可以从一个方法中返回一个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
}
}));

并且可以将其转换为“你的名字”的方法;Aspect作为变量。或者像其他答案一样,使用一个单独的带有构造函数的类。