Denominazione di thread e pool di thread di ExecutorService


228

Diciamo che ho un'applicazione che utilizza il Executorframework in quanto tale

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

Quando eseguo l'applicazione nel debugger, un thread viene creato con il seguente (default) nome: Thread[pool-1-thread-1]. Come puoi vedere, questo non è tremendamente utile e, per quanto ne so, il Executorframework non fornisce un modo semplice per nominare i thread o i pool di thread creati.

Quindi, come si fa a fornire nomi per i thread / pool di thread? Per esempio, Thread[FooPool-FooThread].

Risposte:


118

Potresti fornire un ThreadFactorya newSingleThreadScheduledExecutor(ThreadFactory threadFactory). La fabbrica sarà responsibe per la creazione di thread e sarà in grado di nominarli.

Per citare il Javadoc :

Creazione di nuovi thread

I nuovi thread vengono creati utilizzando a ThreadFactory. Se non diversamente specificato, Executors.defaultThreadFactory()viene utilizzato a, che crea thread in modo che siano tutti uguali ThreadGroupe con la stessa NORM_PRIORITYpriorità e lo stato non daemon. Fornendo un diverso ThreadFactory, è possibile modificare il nome del thread, il gruppo di thread, la priorità, lo stato del daemon, ecc. Se ThreadFactorynon si riesce a creare un thread quando viene richiesto restituendo null da newThread, l'esecutore continuerà, ma potrebbe non essere in grado di eseguire alcuna attività


283

La guava ha quasi sempre ciò di cui hai bisogno .

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

e passalo al tuo ExecutorService.


3
È fantastico!
Martin Vseticka,

25
È triste! :-(
exic

Non sono sicuro dove trovare "guava". Ci sono molte parti di Guava di Google e ci sono dozzine di librerie con lo stesso nome. Suppongo che intendi search.maven.org/artifact/com.google.guava/guava/29.0-jre/… . È giusto? Il link che fornisci suggerisce che provenga da Google, ma Google ha anche una mezza dozzina di artefatti su Maven / Sonatype chiamato "guava".
Jason,

@Jason - Se stai scrivendo un progetto Java non banale, molto probabilmente dovresti già avere guava come dipendenza. Ed eccolo qui: github.com/google/guava
pathikrit

@pathikrit, grazie! Penso di dover studiare di più su Guava :-)
Jason

95

Puoi provare a fornire il tuo thread factory, che creerà thread con nomi appropriati. Ecco un esempio:

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

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

58

Puoi anche cambiare il nome del tuo thread in seguito, mentre il thread viene eseguito:

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

Ciò potrebbe essere interessante se, ad esempio, stai usando lo stesso ThreadFactory per diversi tipi di attività.


7
Questo ha funzionato bene perché, come descritto da FlorianT, ho molti tipi diversi di thread e non volevo creare più oggetti ThreadFactory solo per il nome. Ho chiamato Thread.currentThread (). SetName ("FooName"); come prima riga di ogni metodo run ().
Robin Zimmermann,

5
Un problema minore con questo è quando si verifica il comportamento a rottura descritto nella documentazione: (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.). Se ExecutorService sostituisce il thread, verrà nominato da ThreadFactory. Inoltre, vedere il nome scomparire durante il debug potrebbe essere un indicatore utile.
sethro,

Semplicemente fantastico! Grazie.
chiede il

1
Come dice l'altra risposta, questo è un metodo rapido e sporco per impostare il nome, e se lo fai con più thread, tutti avranno lo stesso nome !!
Tano,

Potrebbe voler reimpostare il nome del thread su originale all'uscita, perché potrebbe conservare il nome anche se sta lavorando su attività non correlate diverse.
Dustin K,

51

Il BasicThreadFactoryfrom apache commons-lang è anche utile per fornire il comportamento di denominazione. Invece di scrivere una classe interna anonima, puoi usare Builder per nominare i thread come desideri. Ecco l'esempio del 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

Se si utilizza Spring, è CustomizableThreadFactorypossibile impostare un prefisso per il nome della discussione.

Esempio:

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

In alternativa, puoi creare il tuo ExecutorServicecome bean Spring usando ThreadPoolExecutorFactoryBean- quindi i thread saranno tutti nominati con il beanName-prefisso.

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

Nell'esempio sopra, i thread saranno nominati con myExecutor-prefisso. È possibile impostare esplicitamente il prefisso su un valore diverso (ad es. "myPool-") Impostando executorFactoryBean.setThreadNamePrefix("myPool-")il bean di fabbrica.


non riesci a trovare CustomizableThreadFactory? sto usando jdk 1.7. qualche idea di cosa mi sto perdendo qui?
Kamran Shahid,

@KamranShahid questa è una classe di Spring Framework, devi usare Spring per averla
Adam Michalik,

20

C'è un RFE aperto per questo con Oracle. Dai commenti del dipendente Oracle sembra che non capiscano il problema e non risolveranno. È una di queste cose che è semplicissima da supportare nel JDK (senza rompere la compatibilità all'indietro), quindi è un peccato che l'RFE venga frainteso.

Come sottolineato, è necessario implementare la propria ThreadFactory . Se non vuoi inserire Guava o Apache Commons solo per questo scopo, fornisco qui ThreadFactoryun'implementazione che puoi usare. È esattamente simile a quello che si ottiene dal JDK ad eccezione della possibilità di impostare il prefisso del nome thread su qualcosa di diverso da "pool".

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

Quando si desidera utilizzarlo, è sufficiente sfruttare il fatto che tutti i Executorsmetodi consentono di fornire il proprio ThreadFactory.

Questo

    Executors.newSingleThreadExecutor();

fornirà un ExecutorService in cui i thread vengono denominati pool-N-thread-Mma utilizzando

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

otterrai un ExecutorService in cui i thread sono nominati primecalc-N-thread-M. Ecco!


Hai perso una parentesi di chiusura nel tuo ultimo frammento
k.liakos,

Solo una breve nota che SonarLint / Qube preferisce non usare ThreadGroupa favore di ThreadPoolExecutor.
Drakes,

8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

Passa ThreadFactory a un servizio di esecuzione e sei a posto


8

Un metodo rapido e sporco è quello di utilizzare Thread.currentThread().setName(myName);il run()metodo.


7

Estendi ThreadFactory

public interface ThreadFactory

Un oggetto che crea nuovi thread su richiesta. L'uso delle fabbriche di thread rimuove il cablaggio delle chiamate al nuovo thread, consentendo alle applicazioni di utilizzare sottoclassi di thread speciali, priorità, ecc.

Thread newThread(Runnable r)

Crea un nuovo thread. Le implementazioni possono anche inizializzare priorità, nome, stato del daemon, ThreadGroup, ecc.

Codice di esempio:

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

produzione:

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

....eccetera


1
Il thread thread non è thread-safe: dovresti usare un AtomicInteger.
Pino,

Grazie per il suggerimento Ho incorporato il tuo suggerimento.
Ravindra babu,

5

Come già detto in altre risposte, è possibile creare e utilizzare la propria implementazione java.util.concurrent.ThreadFactorydell'interfaccia (non sono necessarie librerie esterne). Sto incollando il mio codice di seguito perché è diverso dalle risposte precedenti poiché utilizza il String.formatmetodo e accetta un nome di base per i thread come argomento del costruttore:

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

E questo è un esempio di utilizzo:

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

EDIT : rendere la mia ThreadFactoryimplementazione thread-safe, grazie a @mchernyakov per averlo sottolineato.
Anche se da nessuna parte nella ThreadFactorydocumentazione si dice che le sue implementazioni devono essere thread-safe, il fatto che DefaultThreadFactorysia thread-safe è un grande suggerimento:

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
Il thread thread (ThreadNum) non è thread-safe, dovresti usare AtomicInteger.
mchernyakov,

Grazie per averlo sottolineato, @mchernyakov ho appena modificato la mia risposta di conseguenza.
Victor Gil,

4

La soluzione Java di base che utilizzo per decorare fabbriche esistenti:

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

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

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

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

3

Puoi scrivere la tua implementazione di ThreadFactory, usando ad esempio alcune implementazioni esistenti (come defaultThreadFactory) e cambiando il nome alla fine.

Esempio di implementazione di 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;
    }
}

E utilizzo:

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

3

Uso per fare lo stesso come sotto (richiede la guavalibreria):

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

1
Vale la pena notare che ThreadFactoryBuilderproviene dalla libreria di Google Guava.
Craig Otis,

3

Trovo più facile usare un lambda come factory di thread se vuoi solo cambiare il nome per un singolo esecutore di thread.

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

questo crea due thread. Uno chiamato "Il tuo nome" e un altro "pool-N-thread-M"
Systemsplanet

@Systemsplanet No, non è così. Prendendo un dump di thread da un esempio minimo che utilizza l'esecutore per eseguire un thread che dorme mostra i seguenti thread:main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

Hum, lo ha fatto quando l'ho provato. Ha senso che, se gli passi un nuovo Runnable (), crea un thread per te e tu stesso stai creando un thread.
Systemsplanet

Mi aspetto che tu abbia usato invece ThreadPoolExecutor o ne avessi uno in esecuzione per qualche altro scopo. Questo codice non creerà un thread "pool-N-thread-M". Inoltre, non credo che abbia senso. La tua affermazione "se gli passi un nuovo Runnable () crea un thread per te" non è corretta. Usa quel runnable per creare un thread e lo fa una volta perché è un esecutore a thread singolo. Viene creato solo 1 thread.
CamW

2

Questa è la mia fabbrica personalizzata che fornisce un nome personalizzato per gli analizzatori di scarico dei thread. Di solito mi limito tf=nulla riutilizzare la fabbrica di thread predefinita JVM. Questo sito Web ha una fabbrica di thread più avanzata.

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

Per tua comodità, questo è un loop dump del thread a scopo di debug.

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

Questo ha funzionato davvero bene per me, un po 'sorpreso che non sia stato votato molto. Ad ogni modo applausi.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.