Disons que j'ai une application qui utilise le framework Executor
en tant que tel
Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}
Lorsque j'exécute cette application dans le débogueur, un thread est créé avec le nom (par défaut) suivant: Thread[pool-1-thread-1]
. Comme vous pouvez le constater, cela n’est pas très utile et, autant que je sache, le cadre Executor
ne permet pas de nommer facilement les threads ou les pools de threads créés.
Alors, comment peut-on fournir des noms pour les threads/thread-pools? Par exemple, Thread[FooPool-FooThread]
.
La goyave a presque toujours ce que vous besoin .
ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()
et le transmettre à votre ExecutorService
.
Vous pouvez essayer de fournir votre propre fabrique de threads, qui créera un thread avec les noms appropriés. Voici un exemple:
class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, "Your name");
}
}
Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);
Vous pouvez également changer le nom de votre fil après, pendant que le fil est exécuté:
Thread.currentThread().setName("FooName");
Cela pourrait être intéressant si, par exemple, vous utilisez le même ThreadFactory pour différents types de tâches.
Le BasicThreadFactory
de Apache commons-lang est également utile pour fournir le comportement de dénomination. Au lieu d'écrire une classe interne anonyme, vous pouvez utiliser le générateur pour nommer les threads comme vous le souhaitez. Voici l'exemple des 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);
Il y a un open RFE pour cela avec Oracle. D'après les commentaires de l'employé Oracle, il semble qu'ils ne comprennent pas le problème et ne résolvent rien. C'est l'une de ces choses qui est extrêmement simple à prendre en charge dans le JDK (sans rompre la compatibilité en amont), il est donc dommage que la RFE soit mal comprise.
Comme indiqué, vous devez implémenter votre propre ThreadFactory . Si vous ne souhaitez pas utiliser Guava ou Apache Commons uniquement à cette fin, je fournis ici une implémentation ThreadFactory
que vous pouvez utiliser. Il est exactement similaire à ce que vous obtenez du JDK, à l'exception de la possibilité de définir le préfixe du nom de fil sur autre chose que "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;
}
}
Lorsque vous voulez l'utiliser, vous profitez simplement du fait que toutes les méthodes Executors
vous permettent de fournir votre propre ThreadFactory
.
Cette
Executors.newSingleThreadExecutor();
donnera un ExecutorService où les threads sont nommés pool-N-thread-M
mais en utilisant
Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc"));
vous obtiendrez un ExecutorService où les threads sont nommés primecalc-N-thread-M
. Voila!
Si vous utilisez Spring, il existe CustomizableThreadFactory
pour lequel vous pouvez définir un préfixe de nom de fil.
Exemple:
ExecutorService alphaExecutor =
Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));
private class TaskThreadFactory implements ThreadFactory
{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "TASK_EXECUTION_THREAD");
return t;
}
}
Passez le ThreadFactory à un service d’exécutant et vous êtes prêt à partir
Une méthode rapide et délicate consiste à utiliser Thread.currentThread().setName(myName);
dans la méthode run()
.
Étendre ThreadFactory
public interface ThreadFactory
Un objet qui crée de nouveaux threads à la demande. L'utilisation de fabriques de threads supprime le câblage d'appels au nouveau thread, ce qui permet aux applications d'utiliser des sous-classes de thread, des priorités, etc.
Thread newThread(Runnable r)
Construit un nouveau thread. Les implémentations peuvent également initialiser la priorité, le nom, l’état du démon, le groupe de threads, etc.
Exemple de 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();
}
}
_
sortie:
_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
Vous pouvez écrire votre propre implémentation de ThreadFactory, en utilisant par exemple une implémentation existante (telle que defaultThreadFactory) et changer le nom à la fin.
Exemple d'implémentation de 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;
}
}
Et utilisation:
Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
Executors.defaultThreadFactory(),
"customName")
);
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());
Runnable getJob() {
return () -> {
// your job
};
}
Ceci est mon usine personnalisée fournissant des noms personnalisés pour les analyseurs de vidage de threads. Habituellement, je donne simplement tf=null
pour réutiliser la fabrique de threads par défaut de la JVM. Ce site a une usine de threads plus avancée.
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") );
Pour votre commodité, il s'agit d'une boucle de vidage de thread à des fins de débogage.
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()
);
}
}
J'ai l'habitude de faire comme ci-dessous (nécessite la bibliothèque guava
):
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);
Je trouve plus facile d’utiliser un lambda en tant que fabrique de threads si vous souhaitez simplement changer le nom d’un exécuteur simple thread.
Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
Comme d'autres réponses l'ont déjà dit, vous pouvez créer et utiliser votre propre implémentation de l'interface Java.util.concurrent.ThreadFactory (aucune bibliothèque externe n'est requise). Je colle mon code ci-dessous car il diffère des réponses précédentes puisqu'il utilise la méthode String.format et prend un nom de base pour les threads en tant qu'argument constructeur:
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));
}
}
Et voici un exemple d'utilisation:
ThreadFactory threadFactory = new NameableThreadFactory("listenerThread");
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);
La solution de base Java que j'ai utilisée pour décorer des usines existantes:
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;
}
}
En action:
Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));