命名ExecutorService的线程和线程池

mre*_*mre 206 java executorservice threadpool runnable managedthreadfactory

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

Executors.newSingleThreadExecutor().submit(new Runnable(){
    @Override
    public void run(){
        // do stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

pat*_*rit 267

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

ThreadFactory namedThreadFactory = 
  new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()
Run Code Online (Sandbox Code Playgroud)

把它传递给你ExecutorService.

  • 那太伤心了!:-( (15认同)
  • 我不知道在哪里可以找到“番石榴”。Google 的 Guava 有很多部分,并且有数十个同名的库。我假设你的意思是https://search.maven.org/artifact/com.google.guava/guava/29.0-jre/bundle。是对的吗?您提供的链接表明它来自 Google,但 Google 在 Maven/Sonatype 上还有大约六个名为“guava”的工件。 (4认同)
  • 如果您不使用 Guava,Apache Commons Lang 也有非常相似的 BasicThreadFactory。 (3认同)
  • 这太妙了! (2认同)

NPE*_*NPE 110

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

引用Javadoc:

创建新线程

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


Nik*_*zov 89

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

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

Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);
Run Code Online (Sandbox Code Playgroud)


Flo*_*anT 51

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

Thread.currentThread().setName("FooName");
Run Code Online (Sandbox Code Playgroud)

如果你使用相同的ThreadFactory来处理不同类型的任务,那么这可能是有意义的.

  • 这很好用,因为正如FlorianT所描述的,我有许多不同类型的线程,并且不想仅为名称创建多个ThreadFactory对象.我调用了Thread.currentThread().setName("FooName"); 作为每个run()方法的第一行. (6认同)
  • 这一个小问题是,当在文档中描述的故障现象时:'(但请注意,如果此单个线程关闭前的执行期间终止由于故障,如果需要执行后续任务的新一个将取而代之. )`.如果ExecutorService替换了该线程,它将由ThreadFactory命名.然后,看到名称消失,而调试可能是一个有用的指标. (5认同)
  • 可能希望在退出时将线程名称设置回原始名称,因为即使它正在处理不同的不相关任务,它也可能保留该名称。 (3认同)

Edw*_*ale 48

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);
Run Code Online (Sandbox Code Playgroud)


Ada*_*lik 23

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

例:

ExecutorService alphaExecutor =
    Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));
Run Code Online (Sandbox Code Playgroud)


pet*_*erh 19

对于Oracle,这是一个开放的RFE.根据Oracle员工的评论,他们似乎不了解这个问题,也无法修复.这是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当您想要使用它时,您只需利用所有Executors方法都允许您提供自己的方法ThreadFactory.

这个

    Executors.newSingleThreadExecutor();
Run Code Online (Sandbox Code Playgroud)

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

    Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc"));
Run Code Online (Sandbox Code Playgroud)

您将获得一个名为线程的ExecutorService primecalc-N-thread-M.瞧!


Víc*_*Gil 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));
    }    
}
Run Code Online (Sandbox Code Playgroud)

这是一个用法示例:

ThreadFactory  threadFactory = new NameableThreadFactory("listenerThread");        
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);
Run Code Online (Sandbox Code Playgroud)

编辑:使我的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)));
    }    
}
Run Code Online (Sandbox Code Playgroud)

  • 你的线程计数器(threadsNum)不是线程安全的,你应该使用AtomicInteger。 (3认同)

I.T*_*ger 8

private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}
Run Code Online (Sandbox Code Playgroud)

将ThreadFactory传递给执行器服务,你很高兴


Rav*_*abu 7

扩展 ThreadFactory

public interface ThreadFactory

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

Thread newThread(Runnable r)

构造一个新的Thread.实现还可以初始化优先级,名称,守护程序状态,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();
    }
 }
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

....等等


小智 6

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


Cam*_*amW 6

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

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
Run Code Online (Sandbox Code Playgroud)

  • 我希望您改用 ThreadPoolExecutor 或出于其他目的运行一个 ThreadPoolExecutor。此代码不会创建“pool-N-thread-M”线程。而且,我不认为这样做确实有意义。您的陈述“如果您传递给它一个新的 Runnable() 它会为您创建一个线程”是不正确的。它使用该可运行对象来创建线程并执行一次,因为它是单线程执行器。仅创建 1 个线程。 (3认同)

Rad*_*ive 6

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

Executors.newFixedThreadPool(10, r -> new Thread(r, "my-threads-%d"))
Run Code Online (Sandbox Code Playgroud)

  • %d 是做什么用的?这应该像其他答案中的 NameableThreadFactory 一样吗? (3认同)

ed2*_*d22 6

使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)


bit*_*ttu 5

我以前做同样的事情(需要guava库):

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


Grz*_*rek 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在行动:

Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));
Run Code Online (Sandbox Code Playgroud)