简体   繁体   English

ExecutorService 的线程和线程池命名

[英]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]

Guava almost always has what you need .番石榴几乎总能满足您的需求

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

and pass it off to your ExecutorService .并将其传递给您的ExecutorService

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, a Executors.defaultThreadFactory() is used, that creates threads to all be in the same ThreadGroup and with the same NORM_PRIORITY priority and non-daemon status.如果没有另外指定,则使用 Executors.defaultThreadFactory Executors.defaultThreadFactory() ,它创建的线程都在同一个ThreadGroup中,并且具有相同的NORM_PRIORITY优先级和非守护进程状态。 By supplying a different ThreadFactory , you can alter the thread's name, thread group, priority, daemon status, etc. If a ThreadFactory fails to create a thread when asked by returning null from newThread , 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.或者,您可以使用ThreadPoolExecutorFactoryBeanExecutorService创建为 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM