Benennen von Threads und Thread-Pools von ExecutorService


227

Angenommen, ich habe eine Anwendung, die das ExecutorFramework als solches verwendet

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

Wenn ich diese Anwendung im Debugger ausführe, wird ein Thread mit dem folgenden (Standard-) Namen erstellt : Thread[pool-1-thread-1]. Wie Sie sehen können, ist dies nicht besonders nützlich, und soweit ich das beurteilen kann, bietet das ExecutorFramework keine einfache Möglichkeit, die erstellten Threads oder Thread-Pools zu benennen.

Wie geht man also vor, um Namen für die Threads / Thread-Pools bereitzustellen? Zum Beispiel Thread[FooPool-FooThread].

Antworten:


117

Sie könnten ein ThreadFactoryzu liefern newSingleThreadScheduledExecutor(ThreadFactory threadFactory). Die Factory ist für die Erstellung von Threads verantwortlich und kann diese benennen.

Um den Javadoc zu zitieren :

Neue Threads erstellen

Neue Threads werden mit a erstellt ThreadFactory. Wenn nicht anders angegeben, Executors.defaultThreadFactory()wird a verwendet, das Threads erstellt, die sich alle in derselben ThreadGroupund mit derselben NORM_PRIORITYPriorität und demselben Nicht-Daemon-Status befinden. Durch Angabe eines anderen ThreadFactorykönnen Sie den Namen, die Thread-Gruppe, die Priorität, den Daemon-Status usw. des Threads ändern. Wenn ein ThreadFactoryThread bei der Rückgabe von null newThreadnicht erstellt werden kann, fährt der Executor fort, kann jedoch möglicherweise keine Aufgaben ausführen


282

Guave hat fast immer das, was Sie brauchen .

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

und gib es an deine weiter ExecutorService.


3
Das ist fantastisch!
Martin Vseticka

24
Das ist traurig! :-(
exic

Ich bin mir nicht sicher, wo ich "Guave" finden kann. Googles Guave enthält viele Teile und Dutzende gleichnamiger Bibliotheken. Ich nehme an, Sie meinen search.maven.org/artifact/com.google.guava/guava/29.0-jre/… . Ist das richtig? Der von Ihnen bereitgestellte Link deutet darauf hin, dass er von Google stammt, aber Google hat auch etwa ein halbes Dutzend Artefakte auf Maven / Sonatype mit dem Namen "Guave".
Jason

@Jason - Wenn Sie ein nicht triviales Java-Projekt schreiben, sollten Sie wahrscheinlich bereits Guave als Abhängigkeit haben. Und hier ist es: github.com/google/guava
pathikrit

@ Pathikrit, danke! Ich denke, ich muss mehr über Guave lernen :-)
Jason

95

Sie können versuchen, eine eigene Thread-Factory bereitzustellen, die einen Thread mit den entsprechenden Namen erstellt. Hier ist ein Beispiel:

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

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

58

Sie können den Namen Ihres Threads auch später ändern, während der Thread ausgeführt wird:

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

Dies könnte von Interesse sein, wenn Sie beispielsweise dieselbe ThreadFactory für verschiedene Arten von Aufgaben verwenden.


7
Dies hat gut funktioniert, da ich, wie FlorianT beschrieben hat, viele verschiedene Arten von Threads habe und nicht mehrere ThreadFactory-Objekte nur für den Namen erstellen wollte. Ich habe Thread.currentThread () aufgerufen. SetName ("FooName"); als erste Zeile in jeder run () -Methode.
Robin Zimmermann

5
Ein kleines Problem dabei ist, wenn das in den Dokumenten beschriebene Fehlerverhalten auftritt : (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.). Wenn der ExecutorService den Thread ersetzt, wird er von der ThreadFactory benannt. Andererseits könnte es ein nützlicher Indikator sein, den Namen beim Debuggen verschwinden zu sehen.
Sethro

Einfach super! Danke dir.
Asgs

1
Wie die andere Antwort sagt, ist dies eine schnelle und schmutzige Methode, um den Namen festzulegen. Wenn Sie dies mit mehreren Threads tun, haben alle den gleichen Namen !!
Tano

Möglicherweise möchten Sie den Thread-Namen beim Beenden auf das Original zurücksetzen, da der Name möglicherweise beibehalten wird, auch wenn er an verschiedenen nicht verwandten Aufgaben arbeitet.
Dustin K

51

Die BasicThreadFactoryfrom apache commons-lang ist auch nützlich, um das Benennungsverhalten bereitzustellen. Anstatt eine anonyme innere Klasse zu schreiben, können Sie den Builder verwenden, um die Threads nach Ihren Wünschen zu benennen. Hier ist das Beispiel aus den 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

Wenn Sie Spring verwenden, CustomizableThreadFactorykönnen Sie dort ein Thread-Namenspräfix festlegen.

Beispiel:

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

Alternativ können Sie Ihre ExecutorServiceals Spring Bean mit erstellen ThreadPoolExecutorFactoryBean- dann werden alle Threads mit dem beanName-Präfix benannt.

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

Im obigen Beispiel werden die Threads mit dem myExecutor-Präfix benannt. Sie können das Präfix explizit auf einen anderen Wert setzen (z. B. "myPool-"), indem Sie es executorFactoryBean.setThreadNamePrefix("myPool-")auf der Factory Bean festlegen .


kann CustomizableThreadFactory nicht finden? Ich benutze JDK 1.7. Irgendeine Idee, was ich hier vermisse?
Kamran Shahid

@KamranShahid Dies ist eine Spring Framework-Klasse. Sie müssen Spring verwenden, um sie zu haben
Adam Michalik

20

Dafür gibt es bei Oracle eine offene RFE . Aus den Kommentaren des Oracle-Mitarbeiters geht hervor, dass er das Problem nicht versteht und es nicht beheben kann. Es ist eines dieser Dinge, das im JDK kinderleicht zu unterstützen ist (ohne die Abwärtskompatibilität zu beeinträchtigen). Es ist also eine Schande, dass die RFE missverstanden wird.

Wie bereits erwähnt, müssen Sie Ihre eigene ThreadFactory implementieren . Wenn Sie Guava oder Apache Commons nicht nur für diesen Zweck verwenden möchten, stelle ich hier eine ThreadFactoryImplementierung zur Verfügung, die Sie verwenden können. Es ist genau dem ähnlich, was Sie vom JDK erhalten, mit der Ausnahme, dass das Thread-Namenspräfix auf etwas anderes als "Pool" gesetzt werden kann.

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

Wenn Sie es verwenden möchten, nutzen Sie einfach die Tatsache, dass Sie mit allen ExecutorsMethoden Ihre eigenen bereitstellen können ThreadFactory.

Dies

    Executors.newSingleThreadExecutor();

gibt einen ExecutorService, in dem Threads benannt werden, pool-N-thread-Maber mit

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

Sie erhalten einen ExecutorService, in dem Threads benannt sind primecalc-N-thread-M. Voila!


Sie haben eine schließende Klammer in Ihrem letzten Ausschnitt verpasst
k.liakos

Nur eine kurze Anmerkung, die SonarLint / Qube lieber nicht ThreadGroupzugunsten von verwendet ThreadPoolExecutor.
Drakes

8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

Übergeben Sie die ThreadFactory an einen Executorservice und Sie können loslegen


8

Ein schneller und schmutziger Weg ist die Verwendung Thread.currentThread().setName(myName);in der run()Methode.


7

Erweitern Sie ThreadFactory

public interface ThreadFactory

Ein Objekt, das bei Bedarf neue Threads erstellt. Durch die Verwendung von Thread-Fabriken wird die Festverdrahtung von Aufrufen an einen neuen Thread entfernt, sodass Anwendungen spezielle Thread-Unterklassen, Prioritäten usw. verwenden können.

Thread newThread(Runnable r)

Erstellt einen neuen Thread. Implementierungen können auch Priorität, Name, Dämonstatus, ThreadGroup usw. initialisieren.

Beispielcode:

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

Ausgabe:

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


1
Ihr Thread-Zähler ist nicht threadsicher: Sie sollten eine AtomicInteger verwenden.
Pino

Danke für den Vorschlag. Ich habe Ihren Vorschlag aufgenommen.
Ravindra Babu

5

Wie bereits in anderen Antworten erwähnt, können Sie eine eigene Implementierung der java.util.concurrent.ThreadFactorySchnittstelle erstellen und verwenden (keine externen Bibliotheken erforderlich). Ich füge meinen Code unten ein, weil er sich von den vorherigen Antworten unterscheidet, da er eine String.formatMethode verwendet und einen Basisnamen für die Threads als Konstruktorargument verwendet:

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

Und dies ist ein Anwendungsbeispiel:

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

BEARBEITEN : Ich mache meine ThreadFactoryImplementierung threadsicher, danke an @mchernyakov für den Hinweis.
Obwohl nirgends in der ThreadFactoryDokumentation gesagt wird, dass seine Implementierungen threadsicher sein müssen, ist die Tatsache, dass die DefaultThreadFactorythreadsicher ist, ein großer Hinweis:

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
Ihr Thread-Zähler (threadsNum) ist nicht threadsicher, Sie sollten AtomicInteger verwenden.
Mchernyakov

Vielen Dank für den Hinweis, @mchernyakov Ich habe gerade meine Antwort entsprechend bearbeitet.
Víctor Gil

4

Die selbst entwickelte Java-Kernlösung, mit der ich vorhandene Fabriken dekoriere:

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 Aktion:

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

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

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

3

Sie können Ihre eigene Implementierung von ThreadFactory schreiben, indem Sie beispielsweise eine vorhandene Implementierung (wie defaultThreadFactory) verwenden und den Namen am Ende ändern.

Beispiel für die Implementierung von 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;
    }
}

Und Verwendung:

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

3

Ich mache das gleiche wie unten (erfordert guavaBibliothek):

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

1
Erwähnenswert ist, dass dies ThreadFactoryBuilderaus der Google Guava-Bibliothek stammt.
Craig Otis

3

Ich finde es am einfachsten, ein Lambda als Thread-Factory zu verwenden, wenn Sie nur den Namen für einen einzelnen Thread-Executor ändern möchten.

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

Dadurch werden zwei Threads erstellt. Einer mit dem Namen "Your name" und ein anderer mit dem Namen "pool-N-thread-M"
Systemsplanet

@ Systemsplanet Nein, das tut es nicht. Wenn Sie einen Thread-Dump aus einem minimalen Beispiel entnehmen, in dem der Executor einen Thread im main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
Ruhezustand ausführt

Hum, es tat, als ich es versuchte. Es ist sinnvoll, dass es einen Thread für Sie erstellt, wenn Sie ihm ein neues Runnable () übergeben, und Sie selbst einen Thread erstellen.
Systemsplanet

Ich gehe davon aus, dass Sie stattdessen einen ThreadPoolExecutor verwendet haben oder einen für einen anderen Zweck ausgeführt haben. Dieser Code erstellt keinen "Pool-N-Thread-M" -Thread. Ich glaube auch nicht, dass es Sinn macht, dass es so wäre. Ihre Aussage "Wenn Sie ein neues Runnable () übergeben, wird ein Thread für Sie erstellt" ist nicht korrekt. Es verwendet diese ausführbare Datei, um einen Thread zu erstellen, und tut dies einmal, da es sich um einen Single-Thread-Executor handelt. Es wird nur 1 Thread erstellt.
CamW

2

Dies ist meine angepasste Fabrik, die angepasste Namen für Thread-Dump-Analysatoren bereitstellt. Normalerweise gebe ich nur, tf=nullum die JVM-Standard-Thread-Factory wiederzuverwenden. Diese Website verfügt über eine erweiterte 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") );

Für Ihre Bequemlichkeit ist dies eine Thread-Dump-Schleife für Debug-Zwecke.

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

Das hat wirklich gut für mich funktioniert, etwas überrascht, dass es nicht viel positiv bewertet wurde. So oder so Prost.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.