web-dev-qa-db-fra.com

Utilisation de ThreadFactory en Java

Quelqu'un peut-il expliquer brièvement comment et quand utiliser un threadfactory? Un exemple avec et sans utilisation de ThreadFactory peut être très utile pour comprendre les différences.

Merci! 

50
jagamot

Le modèle d'usine est un modèle de création utilisé dans le développement logiciel pour encapsuler les processus impliqués dans la création d'objets.

Supposons que nous avons des threads de travail pour différentes tâches et que nous les voulons avec des noms spéciaux (par exemple, à des fins de débogage). Nous pourrions donc implémenter un ThreadFactory:

public class WorkerThreadFactory implements ThreadFactory {
   private int counter = 0;
   private String prefix = "";

   public WorkerThreadFactory(String prefix) {
     this.prefix = prefix;
   }

   public Thread newThread(Runnable r) {
     return new Thread(r, prefix + "-" + counter++);
   }
}

Si vous aviez une telle exigence, il serait assez difficile de l'implémenter sans modèle d'usine ou constructeur.


ThreadFactory fait partie de l'API Java car il est également utilisé par d'autres classes. Ainsi, l’exemple ci-dessus montre pourquoi nous devrions parfois utiliser «une usine pour créer des threads», mais il n’est bien entendu absolument pas nécessaire de mettre en œuvre Java.util.concurrent.ThreadFactory pour accomplir cette tâche.

43
Andreas_D

Voici une utilisation possible. Si vous disposez d'un service exécuteur qui exécute vos tâches exécutables de manière multithread et que de temps en temps votre thread meurt d'une exception non interceptée. Supposons que vous ne souhaitiez pas enregistrer toutes ces exceptions. ThreadFactory résout ce problème:

ExecutorService executor = Executors.newSingleThreadExecutor(new LoggingThreadFactory());

executor.submit(new Runnable() {
   @Override
   public void run() {
      someObject.someMethodThatThrowsRuntimeException();
   }
});

LoggingThreadFactory peut être implémenté comme celui-ci:

public class LoggingThreadFactory implements ThreadFactory
{

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

        t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
        {
            @Override
            public void uncaughtException(Thread t, Throwable e)
            {
                LoggerFactory.getLogger(t.getName()).error(e.getMessage(), e);
            }
        });

        return t;
    }
}
57
Boris Pavlović

Quelques rouages ​​

Le sujet est assez bien couvert, à l'exception de quelques travaux internes qui ne sont pas facilement visibles . Lors de la création d'un thread avec le constructeur, le thread nouvellement créé hérite des threads actuels:

  • ThreadGroup (sauf si fourni ou System.getSecurityManager().getThreadGroup() renvoie ThreadGroup arbitraire) - Le groupe de threads lui-même peut être important dans certains cas et peut entraîner une interruption/interruption incorrecte du thread. La ThreadGroup sera le gestionnaire d'exceptions par défaut.
  • ContextClassLoader - dans un environnement géré, cela ne devrait pas être un gros problème, car l'environnement doit basculer le CCL, mais si vous voulez l'implémenter, gardez cela à l'esprit. La fuite du CCL de l'appelant est assez mauvaise, tout comme le groupe de threads (en particulier si le threadGroup est une sous-classe et non pas un Java.lang.ThreadGroup direct - il est nécessaire de remplacer le ThreadGroup.uncaughtException)
  • AccessControlContext - ici, il n'y a pratiquement rien à faire (à part démarrer dans un thread dédié) car le champ est réservé à un usage interne, et très peu soupçonnent même l'existence de.
  • taille de la pile (généralement non spécifiée, mais il peut être très difficile d'obtenir un thread avec une taille de pile très étroite, en fonction de l'appelant)
  • priorité - la plupart des gens connaissent et ont tendance à le définir (plus ou moins)
  • statut de démon - généralement peu important et facilement perceptible (si l'application disparaît)
  • Enfin, le thread hérite de la variable InheritableThreadLocal de l'appelant, ce qui peut (ou non) avoir des conséquences. Encore une fois, rien ne peut être fait, à part créer le fil dans un fil dédié.

En fonction de l'application, les points ci-dessus peuvent n'avoir aucun effet, mais dans certains cas, certains d'entre eux peuvent entraîner des fuites de classe/ressources difficiles à détecter et présentant un comportement non déterministe.


Cela ferait un très long post mais si ...

Ci-dessous, quelques exemples (espérons-le) de code réutilisable pour ThreadFactoryimplementation, il peut être utilisé dans des environnements gérés pour garantir que ThreadGroup (qui peut limiter la priorité ou interrompre les threads), ContextClassLoader, stacksize, etc. être configuré) et non fui. S'il y a un quelconque intérêt, je peux montrer comment traiter avec ThreadLocals hérité ou l'accusé hérité (qui peut essentiellement faire fuir l'appel classloader)

package bestsss.util;

import Java.lang.Thread.UncaughtExceptionHandler;
import Java.security.AccessControlContext;
import Java.security.AccessController;
import Java.security.PrivilegedAction;
import Java.util.concurrent.ThreadFactory;
import Java.util.concurrent.atomic.AtomicLong;

public class ThreadFactoryX implements ThreadFactory{
    //thread properties
    long stackSize;
    String pattern;
    ClassLoader ccl;
    ThreadGroup group;
    int priority;
    UncaughtExceptionHandler exceptionHandler;
    boolean daemon;

    private boolean configured;

    private boolean wrapRunnable;//if acc is present wrap or keep it
    protected final AccessControlContext acc;

    //thread creation counter
    protected final AtomicLong counter = new AtomicLong();

    public ThreadFactoryX(){        
        final Thread t = Thread.currentThread();
        ClassLoader loader;
    AccessControlContext acc = null;
    try{
        loader =  t.getContextClassLoader();
        if (System.getSecurityManager()!=null){
            acc = AccessController.getContext();//keep current permissions             
            acc.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
    }catch(SecurityException _skip){
        //no permission
        loader =null;
        acc = null;
    }

    this.ccl = loader;
    this.acc = acc;
    this.priority = t.getPriority();    
    this.daemon = true;//Executors have it false by default

    this.wrapRunnable = true;//by default wrap if acc is present (+SecurityManager)

    //default pattern - caller className
    StackTraceElement[] stack =  new Exception().getStackTrace();    
    pattern(stack.length>1?getOuterClassName(stack[1].getClassName()):"ThreadFactoryX", true);     
    }

    public ThreadFactory finishConfig(){
        configured = true;
        counter.addAndGet(0);//write fence "w/o" volatile
        return this;
    }

    public long getCreatedThreadsCount(){
        return counter.get();
    }

    protected void assertConfigurable(){
        if (configured)
            throw new IllegalStateException("already configured");
    }

    private static String getOuterClassName(String className){
        int idx = className.lastIndexOf('.')+1;
        className = className.substring(idx);//remove package
        idx = className.indexOf('$');
        if (idx<=0){
            return className;//handle classes starting w/ $
        }       
        return className.substring(0,idx);//assume inner class

    }

    @Override
    public Thread newThread(Runnable r) {
        configured = true;
        final Thread t = new Thread(group, wrapRunnable(r), composeName(r), stackSize);
        t.setPriority(priority);
        t.setDaemon(daemon);
        t.setUncaughtExceptionHandler(exceptionHandler);//securityException only if in the main group, shall be safe here
        //funny moment Thread.getUncaughtExceptionHandler() has a race.. badz (can throw NPE)

        applyCCL(t);
        return t;
    }

    private void applyCCL(final Thread t) {
        if (ccl!=null){//use factory creator ACC for setContextClassLoader
            AccessController.doPrivileged(new PrivilegedAction<Object>(){
                @Override
                public Object run() {
                    t.setContextClassLoader(ccl);
                    return null;
                }                               
            }, acc);        
        }
    }
    private Runnable wrapRunnable(final Runnable r){
        if (acc==null || !wrapRunnable){
            return r;
        }
        Runnable result = new Runnable(){
            public void run(){
                AccessController.doPrivileged(new PrivilegedAction<Object>(){
                    @Override
                    public Object run() {
                        r.run();
                        return null;
                    }                               
                }, acc);
            }
        };
        return result;      
    }


    protected String composeName(Runnable r) {
        return String.format(pattern, counter.incrementAndGet(), System.currentTimeMillis());
    }   


    //standard setters allowing chaining, feel free to add normal setXXX    
    public ThreadFactoryX pattern(String patten, boolean appendFormat){
        assertConfigurable();
        if (appendFormat){
            patten+=": %d @ %tF %<tT";//counter + creation time
        }
        this.pattern = patten;
        return this;
    }


    public ThreadFactoryX daemon(boolean daemon){
        assertConfigurable();
        this.daemon = daemon;
        return this;
    }

    public ThreadFactoryX priority(int priority){
        assertConfigurable();
        if (priority<Thread.MIN_PRIORITY || priority>Thread.MAX_PRIORITY){//check before actual creation
            throw new IllegalArgumentException("priority: "+priority);
        }
        this.priority = priority;
        return this;
    }

    public ThreadFactoryX stackSize(long stackSize){
        assertConfigurable();
        this.stackSize = stackSize;
        return this;
    }


    public ThreadFactoryX threadGroup(ThreadGroup group){
        assertConfigurable();
        this.group= group;
        return this;        
    }

    public ThreadFactoryX exceptionHandler(UncaughtExceptionHandler exceptionHandler){
        assertConfigurable();
        this.exceptionHandler= exceptionHandler;
        return this;                
    }

    public ThreadFactoryX wrapRunnable(boolean wrapRunnable){
        assertConfigurable();
        this.wrapRunnable= wrapRunnable;
        return this;                        
    }

    public ThreadFactoryX ccl(ClassLoader ccl){
        assertConfigurable();
        this.ccl = ccl;
        return this;
    }
}

Aussi, quelques utilisations très simples:

ThreadFactory factory = new TreadFactoryX().priority(3).stackSize(0).wrapRunnable(false).pattern("Socket workers", true).
daemon(false).finishConfig();
16
bestsss

IMHO la fonction la plus importante d'une ThreadFactory est de nommer les threads quelque chose d'utile. Avoir des threads dans un stacktrace nommé pool-1-thread-2 ou pire Thread-12 est une douleur complète lors du diagnostic des problèmes.

Bien sûr, le fait d'avoir ThreadGroup, le statut et la priorité d'un démon sont également utiles.

4
Jed Wesley-Smith

Utilisation de ThreadFactory en Java 

Un objet qui crée de nouveaux threads à la demande. L'utilisation de fabriques de threads supprime le câblage des appels à new Thread, ce qui permet aux applications d'utiliser des sous-classes de thread, des priorités, etc.

La mise en oeuvre la plus simple de cette interface est la suivante:

class SimpleThreadFactory implements ThreadFactory {
   public Thread newThread(Runnable r) {
     return new Thread(r);
   }
 }

DefaultThreadFactory de ThreadPoolExecutor.Java

static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

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

La source

2
Premraj

Il est toujours recommandé d'utiliser une fabrique de threads personnalisée. Les usines par défaut ne sont pas très utiles. Vous devez utiliser une fabrique personnalisée pour les raisons suivantes: 

  1. Pour avoir des noms de fil personnalisés
  2. Pour choisir entre les types de fil
  3. Pour choisir la priorité du fil
  4. Pour gérer les exceptions non capturées

Vérifiez ce post: http://wilddiary.com/understanding-Java-threadfactory-creating-custom-thread-factories/

2
Drona

Comme mentionné par "InsertNickHere", vous devrez comprendre le Factory Pattern .

Un bon exemple d'utilisation d'une ThreadFactory est le ThreadPoolExecutor : L'exécuteur créera des Threads si nécessaire et s'occupera du pooling. Si vous souhaitez intervenir dans le processus de création et attribuer des noms spéciaux aux threads créés, ou les affecter à un groupe de threads, vous pouvez créer une unité de threads à cette fin et le donner à l'exécuteur.

C'est un peu IoC - style.

2
Hardcoded

ThreadFactory est une interface avec une seule méthode public abstract Java.lang.Thread newThread(Java.lang.Runnable arg0);

Son utilisation dépend de vos besoins. Supposons que vous souhaitiez qu'une fonctionnalité particulière crée toujours des threads Daemon. Vous pouvez facilement y parvenir avec ThreadFactory.

Le code ci-dessous est juste pour dire le fondamental. Il ne fait aucune fonctionnalité spécifique.

package TestClasses;
import Java.util.concurrent.ThreadFactory;
public class ThreadFactoryEx implements ThreadFactory{
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

package TestClasses;
import Java.util.concurrent.ThreadPoolExecutor;
public class RunnableEx implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 5; i++) {
            System.out.println("in a loop" + i + "times");
        }
    }
}


package TestClasses;

import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;

public class Thread1 {
    public static void main(String[] args) {
        ExecutorService exe = Executors.newCachedThreadPool(new ThreadFactoryEx());
        for (int i = 0; i < 4; i++) {
            exe.execute(new RunnableEx());
        }
    }
}
1
Aalekh

ThreadFactory sera utile 

  • pour définir un nom de fil plus descriptif 
  • définir le statut du démon de thread 
  • pour définir la priorité du thread

Vous pouvez utiliser ThreadFactoryBuilder à partir de Google Guava Lib pour créer ThreadFactory comme ceci

ThreadFactory threadFactory = new ThreadFactoryBuilder()
        .setNameFormat("MyThreadPool-Worker-%d")
        .setDaemon(true)
        .build();
0
ravthiru

Examinez VerboseThreads (implémente ThreadFactory) à partir de jcabi-log . Cette implémentation crée Threads qui enregistre les exceptions lorsqu'elles sont supprimées. Classe très utile lorsque vous avez besoin de savoir quand et pourquoi vos fils meurent.

0
yegor256