命名ExecutorService的线程和线程池


227

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

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

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

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

Answers:


117

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

引用Javadoc

创建新线程

使用创建新线程ThreadFactory。如果没有另外指定,Executors.defaultThreadFactory()则使用a,它将创建所有线程,并且所有线程都ThreadGroup具有相同的NORM_PRIORITY优先级和非守护程序状态。通过提供其他参数ThreadFactory,您可以更改线程的名称,线程组,优先级,守护程序状态等。如果在ThreadFactory从返回null时被要求创建线程失败newThread,执行器将继续执行,但可能无法执行任何任务


282

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

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

并传递给你的朋友ExecutorService


3
这太妙了!
Martin Vseticka '16

25
真伤心!:-(
exic

我不确定在哪里可以找到“番石榴”。Google的番石榴有很多部分,而且有数十个同名的库。我假设您的意思是search.maven.org/artifact/com.google.guava/guava/29.0-jre/…。那正确吗?您提供的链接表明它来自Google,但Google在Maven / Sonatype上还有大约六个工件,名为“ guava”。
杰森

@Jason-如果您正在编写一个普通的Java项目,则您很可能应该已经将番石榴作为依赖项。这是:github.com/google/guava
pathikrit

@pathikrit,谢谢!我认为我需要进一步研究番石榴:-)
杰森

95

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

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

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

58

执行线程后,还可以在之后更改线程的名称:

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

例如,如果您将同一ThreadFactory用于不同类型的任务,则可能会对此感兴趣。


7
这很好用,因为正如FlorianT所描述的,我有许多不同类型的线程,并且不想只为该名称创建多个ThreadFactory对象。我叫Thread.currentThread()。setName(“ FooName”); 作为每个run()方法的第一行。
罗宾·齐默尔曼

5
与此相关的一个小问题是文档中描述的失败行为发生时:(Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.)。如果ExecutorService替换了线程,则它将由ThreadFactory命名。再说一次,在调试时看到名称消失可能是一个有用的指示。
sethro 2014年

简直太棒了!谢谢。
ass 2015年

1
就像另一个答案所说的那样,这是一种快速而肮脏的设置名称的方法,如果使用多个线程进行设置,则所有名称都将相同!
塔诺2015年

可能希望在退出时将线程名称设置回原始名称,因为即使它正在执行其他不相关的任务,它也可能保留该名称。
达斯汀K

51

BasicThreadFactoryApache的公地郎也提供命名行为非常有用。无需编写匿名内部类,而是可以使用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);

30

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

例:

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

另外,您也可以ExecutorService使用ThreadPoolExecutorFactoryBean-将其创建为Spring Bean ,然后所有线程都将以beanName-前缀命名。

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

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


找不到CustomizableThreadFactory?我正在使用JDK 1.7。知道我在这里想念什么吗?
卡姆兰·沙希德

@KamranShahid这是一个Spring Framework类,您必须使用Spring才能拥有它
Adam Michalik

20

Oracle为此提供了开放的RFE。从Oracle员工的评论看来,他们似乎不了解该问题,也无法解决。在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。瞧!


您在最后一个片段中错过了右圆括号
k.liakos

请注意,SonarLint / Qube ThreadGroup不赞成使用ThreadPoolExecutor
德雷克斯

8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

将ThreadFactory传递给executorservice,您就可以开始工作了


8

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


7

扩展 ThreadFactory

public interface ThreadFactory

根据需要创建新线程的对象。使用线程工厂可以消除对新线程的调用的繁琐工作,从而使应用程序可以使用特殊的线程子类,优先级等。

Thread newThread(Runnable r)

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

样例代码:

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

....等等


1
您的线程计数器不是线程安全的:您应该使用AtomicInteger。
皮诺

感谢您的建议。我已经采纳了你的建议。
Ravindra babu

5

正如其他答案所言,您可以创建并使用自己的 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)));
    }    
}

1
您的线程计数器(threadsNum)不是线程安全的,应该使用AtomicInteger。
mchernyakov

感谢您指出,@ mchernyakov我已经相应地编辑了答案。
维克多·吉尔

4

我用来装饰现有工厂的本地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"));

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

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

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

3

我经常像下面那样做(需要guava库):

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

1
值得注意的ThreadFactoryBuilder是来自Google Guava库。
Craig Otis

3

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

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));

这将创建两个线程。一个名为“您的名字”,另一个名为“ pool-N-thread-M”
Systemsplanet

@Systemsplanet不,不是。从一个使用执行程序运行休眠线程的最小示例中提取线程转储,它显示了以下线程:main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

哼,我尝试的时候就做到了。这是有道理的,因为如果将新的Runnable()传递给它,它将为您创建一个线程,而您自己创建了一个线程。
Systemsplanet

我希望您改用ThreadPoolExecutor或将其运行用于其他目的。此代码不会创建“ pool-N-thread-M”线程。另外,我认为这样做确实没有道理。您的陈述“如果将新的Runnable()传递给它,则会为您创建一个线程”,这是不正确的。它使用该可运行对象创建线程,并且仅执行一次,因为它是单线程执行程序。仅创建一个线程。
CamW

2

这是我的定制工厂,为线程转储分析器提供了定制名称。通常我只是给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()
            );
        }
    }

这对我来说真的很好,有点惊讶它没有得到太多支持。无论哪种方式都可以。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.