267

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

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

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

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

4

20 回答 20

313

番石榴几乎总能满足您的需求

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

并将其传递给您的ExecutorService.

于 2012-03-17T09:06:32.847 回答
133

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

引用Javadoc

创建新线程

新线程是使用ThreadFactory. 如果没有另外指定,Executors.defaultThreadFactory()则使用 a,它创建的线程都处于相同的ThreadGroup、具有相同的NORM_PRIORITY优先级和非守护程序状态。通过提供不同的ThreadFactory,您可以更改线程的名称、线程组、优先级、守护程序状态等。如果ThreadFactory通过返回 null from 询问时无法创建线程newThread,则执行程序将继续,但可能无法执行任何任务

于 2011-05-24T16:38:34.397 回答
104

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

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

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

或者在科特林

Executors.newSingleThreadExecutor { r -> Thread(r, "Your name") }
于 2011-05-24T16:38:07.383 回答
70

您还可以在执行线程时更改线程的名称:

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

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

于 2013-10-31T10:05:57.457 回答
55

from apache commons-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);
于 2011-05-25T14:45:53.017 回答
39

如果您使用的是 Spring,则可以CustomizableThreadFactory为其设置线程名称前缀。

例子:

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

或者,您可以ExecutorService使用 - 创建您的 Spring bean ThreadPoolExecutorFactoryBean,然后所有线程都将使用beanName-前缀命名。

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

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

于 2016-01-07T08:13:59.217 回答
27

Oracle 对此有一个开放的 RFE。从甲骨文员工的评论来看,他们似乎不理解这个问题,也不会解决。这是 JDK 中支持非常简单的事情之一(不会破坏向后兼容性),因此 RFE 被误解是一种耻辱。

正如所指出的,您需要实现自己的ThreadFactory。如果你不想仅仅为了这个目的而引入 Guava 或 Apache Commons,我在这里提供了一个ThreadFactory你可以使用的实现。除了可以将线程名称前缀设置为“池”以外的其他内容之外,它与您从 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。瞧!

于 2015-05-16T18:52:16.340 回答
10

正如其他答案已经说过的那样,您可以创建和使用自己的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)));
    }    
}
于 2019-05-25T11:34:22.577 回答
9

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

于 2014-07-18T07:46:09.413 回答
8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

将 ThreadFactory 传递给 executorservice 就可以了

于 2014-01-08T18:16:39.240 回答
8

扩展 线程工厂

public interface ThreadFactory

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

Thread newThread(Runnable r)

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

示例代码:

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

....ETC

于 2016-02-05T15:59:16.897 回答
8

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

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
于 2019-01-28T10:49:34.653 回答
5

我曾经像下面那样做同样的事情(需要guava库):

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);
于 2018-08-07T09:45:40.583 回答
5

我用来装饰现有工厂的本土核心 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"));
于 2019-01-25T07:03:05.250 回答
4
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());

Runnable getJob() {
        return () -> {
            // your job
        };
}
于 2016-12-03T23:25:22.087 回答
4

基于上面的一些评论,不同的是我只是使用了 lambda

Executors.newFixedThreadPool(10, r -> new Thread(r, "my-threads-%d"))
于 2020-11-20T07:27:41.243 回答
4

使用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;
    }
}
于 2021-01-13T09:43:09.847 回答
3

您可以编写自己的 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")
    );
于 2017-02-06T10:25:41.340 回答
3

这是我的定制工厂,为线程转储分析器提供定制名称。通常我只是给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()
            );
        }
    }
于 2017-08-25T10:39:26.290 回答
1

想我会抛出一些简化的例子,所以选项都在那里:

唯一编号(也可以将其放入方法中):

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

并且可以将其作为“you-name-”方面作为变量的方法。或者像其他答案一样使用带有构造函数的单独类。

于 2021-01-13T18:14:18.430 回答