web-dev-qa-db-ger.com

ThreadFactory-Verwendung in Java

Kann jemand kurz erklären, wie und wann eine ThreadFactory verwendet wird? Ein Beispiel mit und ohne ThreadFactory kann hilfreich sein, um die Unterschiede zu verstehen.

Vielen Dank! 

50
jagamot

Das Factory-Muster ist ein kreatives Entwurfsmuster, das in der Softwareentwicklung verwendet wird, um die Prozesse, die bei der Erstellung von Objekten involviert sind, zu kapseln.

Nehmen wir an, wir haben einige Worker-Threads für verschiedene Aufgaben und möchten, dass sie mit speziellen Namen versehen werden (beispielsweise zu Debugging-Zwecken). So könnten wir eine ThreadFactory implementieren:

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

Wenn Sie eine solche Anforderung hätten, wäre es ziemlich schwierig, sie ohne Factory- oder Builder-Pattern zu implementieren.


ThreadFactory ist Teil der Java-API, da sie auch von anderen Klassen verwendet wird. Das obige Beispiel zeigt also, warum wir in manchen Fällen "eine Factory zum Erstellen von Threads" verwenden sollten. Natürlich ist es nicht notwendig, Java.util.concurrent.ThreadFactory für diese Aufgabe zu implementieren.

43
Andreas_D

Hier ist eine mögliche Verwendung. Wenn Sie über einen Executor-Service verfügen, der Ihre ausführbaren Aufgaben auf Multithreading-Weise ausführt, und von Zeit zu Zeit stirbt Ihr Thread an einer nicht erfassten Ausnahme. Nehmen wir an, Sie wollen all diese Ausnahmen nicht protokollieren. ThreadFactory löst dieses Problem:

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

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

LoggingThreadFactory kann wie folgt implementiert werden:

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ć

Einige innere Funktionen

Das Thema wird ziemlich gut behandelt, mit Ausnahme einiger innerer Arbeiten, die nicht leicht sichtbar sind .. Beim Erstellen eines Threads mit dem Konstruktor erbt der neu erstellte Thread aktuelle Threads:

  • ThreadGroup (sofern nicht angegeben oder System.getSecurityManager().getThreadGroup() willkürlich _ zurückgibt ThreadGroup) - Die Thread-Gruppe selbst kann in einigen Fällen wichtig sein und zu einem falschen Thread-Abbruch/-Abbruch führen. ThreadGroup wird als Standard-Ausnahmehandler verwendet.
  • ContextClassLoader - in einer verwalteten Umgebung sollte dies kein großes Problem sein, da die Umgebung die CCL wechseln soll, aber wenn Sie dies implementieren möchten, denken Sie daran. Das Durchsickern der CCL des Aufrufers ist ziemlich schlecht. Dies gilt auch für die Thread-Gruppe (insbesondere wenn die ThreadGroup eine Unterklasse ist und nicht direkt Java.lang.ThreadGroup - muss ThreadGroup.uncaughtException überschrieben werden.)
  • AccessControlContext - Hier gibt es praktisch nichts zu tun (außer dem Start in einem dedizierten Thread), da das Feld nur für den internen Gebrauch bestimmt ist und nur wenige das Vorhandensein von.
  • stack-Größe (normalerweise nicht angegeben, es kann jedoch ein -fun- ding sein, um einen Thread mit sehr kleiner Stack-Größe zu erhalten, basierend auf dem Aufrufer)
  • priorität - die meisten Menschen kennen und neigen dazu (mehr oder weniger)
  • daemon-Status - normalerweise nicht sehr wichtig und leicht erkennbar (wenn die Anwendung einfach verschwindet)
  • Zu guter Letzt: Der Thread erbt InheritableThreadLocal des Aufrufers - was (möglicherweise) nicht zu einigen Auswirkungen führen kann. Wieder kann nichts getan werden, außer den Thread in einen dedizierten Thread zu laichen.

Abhängig von der Anwendung können die oben genannten Punkte überhaupt keine Auswirkungen haben, aber in einigen Fällen können einige von ihnen zu Klassen-/Ressourcenlecks führen, die schwer zu erkennen sind und kein deterministisches Verhalten zeigen.


Das würde einen extra langen Post machen aber so ...

unten ist ein (hoffentlich) wiederverwendbarer Code für die Implementierung von ThreadFactory. Er kann in verwalteten Umgebungen verwendet werden, um sicherzustellen, dass ThreadGroup (der Threads auf Priorität oder Interrupt beschränken kann), ContextClassLoader , stacksize usw. sind eingestellt (und/oder können konfiguriert werden) und werden nicht durchgesickert. Wenn Interesse besteht, kann ich zeigen, wie mit ThreadLocals vererbt wird oder mit dem geerbten acc (was im Wesentlichen den Aufruf von classloader durchläuft).

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

Auch einige sehr einfache Verwendung:

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

IMHO ist die wichtigste Funktion einer ThreadFactory das Benennen von Threads etwas Nützliches. Threads in einem Stacktrace namens pool-1-thread-2 oder schlechter Thread-12 sind bei der Diagnose von Problemen ein komplizierter Schmerz.

Natürlich haben auch der Status und die Priorität des Daemons eine ThreadGroup.

4

ThreadFactory-Verwendung in Java 

Ein Objekt, das bei Bedarf neue Threads erstellt. Durch die Verwendung von Thread-Factorys werden Aufrufe von new Thread nicht mehr fest verbunden, sodass Anwendungen spezielle Thread-Unterklassen, Prioritäten usw. verwenden können.

Die einfachste Implementierung dieser Schnittstelle ist nur:

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

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

Quelle

2
Premraj

Es ist immer eine gute Praxis, eine benutzerdefinierte Gewindefabrik zu verwenden. Die Standardfabriken sind nicht sehr nützlich. Sie sollten aus folgenden Gründen eine benutzerdefinierte Fabrik verwenden: 

  1. Um benutzerdefinierte Thread-Namen zu haben
  2. Um zwischen den Thread-Typen zu wählen
  3. So wählen Sie die Thread-Priorität aus
  4. Um nicht erfasste Ausnahmen zu behandeln

Überprüfen Sie diesen Beitrag: http://wilddiary.com/understanding-Java-threadfactory-creating-custom-thread-factories/

2
Drona

Wie von "InsertNickHere" erwähnt, müssen Sie das Factory Pattern verstehen.

Ein gutes Beispiel für die Verwendung einer ThreadFactory ist der ThreadPoolExecutor : Der Executor erstellt bei Bedarf Threads und kümmert sich um das Pooling. Wenn Sie beim Erstellungsprozess einspringen und den erstellten Threads spezielle Namen geben oder einer ThreadGroup zuweisen möchten, können Sie eine ThreadFactory für diesen Zweck erstellen und dem Executor übergeben.

Es ist ein bisschen IoC - Stil.

2
Hardcoded

ThreadFactory ist eine Schnittstelle mit einer einzigen Methode public abstract Java.lang.Thread newThread(Java.lang.Runnable arg0);

Die Verwendung hängt von Ihrer Anforderung ab. Angenommen, Sie möchten, dass eine bestimmte Funktionalität immer Daemon-Threads erstellt. Mit ThreadFactory können Sie dies leicht erreichen.

Der folgende Code dient nur zum Erläutern der Grundlagen. Es führt keine spezifische Funktionalität aus.

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 wird nützlich sein 

  • zur Einstellung eines aussagekräftigeren Thread-Namens 
  • um den Thread-Daemon-Status festzulegen 
  • zum Festlegen der Thread-Priorität

Sie können ThreadFactoryBuilder von Google Guava lib verwenden, um ThreadFactory wie folgt zu erstellen

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

Werfen Sie einen Blick auf VerboseThreads (implementiert ThreadFactory) von jcabi-log . Diese Implementierung macht Threads, die Ausnahmen protokolliert, wenn sie aus ihnen herausgeworfen werden. Sehr nützliche Klasse, wenn Sie sehen müssen, wann und warum Ihre Threads sterben.

0
yegor256