Nommer les threads et les pools de threads D'ExecutorService
disons que j'ai une application qui utilise le cadre Executor
comme 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 voir, ce n'est pas très utile et pour autant que je puisse dire, le cadre Executor
ne fournit pas un moyen facile de nommer les threads créés ou les thread-pools.
alors, comment va-t-on fournir des noms pour threads/thread-piscines? Par exemple, Thread[FooPool-FooThread]
.
14 réponses
vous pourriez fournir un ThreadFactory
à newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
. L'usine sera responsable de la création des threads, et pourra les nommer.
pour citer le Javadoc :
créer de nouveaux fils
nouveaux threads sont créés en utilisant un
ThreadFactory
. Si non spécifié ailleurs, unExecutors.defaultThreadFactory()
est utilisé, qui crée les fils à tous être dans le mêmeThreadGroup
et avec le mêmeNORM_PRIORITY
priorité et le statut non-daemon. En fournissant unThreadFactory
différent , vous pouvez modifier le nom du thread, le groupe de thread, la priorité, l'état du démon, etc. Si unThreadFactory
ne crée pas de thread lorsqu'on le lui demande en retournant null DenewThread
, l'exécuteur continuera, mais pourrait ne pas être en mesure d'exécuter des tâches
vous pouvez essayer de fournir votre propre usine de fil, qui créera fil avec des 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 aussi changer le nom de votre thread par la suite, pendant que le thread 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
à partir de apache commons-lang est également utile de prévoir la désignation de comportement. Au lieu d'écrire une classe interne anonyme, vous pouvez utiliser le constructeur pour nommer les fils comme vous voulez. Voici l'exemple de 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é D'Oracle, il semble qu'ils ne comprennent pas le problème et ne vont pas le résoudre. C'est une de ces choses qui est très simple à supporter dans le JDK (sans briser la compatibilité en arrière) donc c'est un peu dommage que le RFE soit mal compris.
comme indiqué, vous devez mettre en œuvre votre propre ThreadFactory . Si vous ne voulez pas tirer dans le Guava ou Apache Commons juste pour cela je fournis ici une implémentation ThreadFactory
que vous pouvez utiliser. Il est exactement similaire à ce que vous obtenez du JDK sauf pour la possibilité de définir le préfixe du nom de thread à 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;
}
}
quand vous voulez l'utiliser vous profitez simplement du fait que toutes les méthodes Executors
vous permettent de fournir votre propre ThreadFactory
.
Ce
Executors.newSingleThreadExecutor();
donnera un Exécutorservice où les threads sont nommés pool-N-thread-M
mais en utilisant
Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc");
vous obtiendrez un Exécutorservice où les threads sont nommés primecalc-N-thread-M
. Voila!
si vous utilisez Spring, il y a 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 executorservice et vous êtes bon à aller
un moyen rapide et sale est d'utiliser Thread.currentThread().setName(myName);
dans la méthode run()
.
Extend ThreadFactory
public interface ThreadFactory
un objet qui crée de nouveaux threads à la demande. L'utilisation des usines de fil élimine le durcissement des appels à nouveau fil, permettant aux applications d'utiliser des sous-classes spéciales de fil, des priorités, etc.
Thread newThread(Runnable r)
construit un nouveau Thread. Les implémentations peuvent aussi initialiser la priorité, le nom, le statut de démon, le ThreadGroup, etc.
code échantillon:
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
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());
Runnable getJob() {
return () -> {
// your job
};
}
vous pouvez écrire votre propre implémentation de ThreadFactory, en utilisant par exemple une implémentation existante (comme defaultThreadFactory) et changer le nom à la fin.
exemple de mise en œuvre 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 usage:
Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
Executors.defaultThreadFactory(),
"customName")
);
c'est mon usine personnalisée fournissant un nom personnalisé pour les analyseurs de dump filetage. En général, je donne juste tf=null
pour réutiliser JVM default thread factory. ce site Web a plus avancé l'usine de fil.
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 la même chose comme ci-dessous:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);