Использование ThreadFactory в Java - PullRequest
51 голосов
/ 05 июля 2010

Может кто-нибудь кратко объяснить, КАК и КОГДА использовать ThreadFactory? Пример с использованием ThreadFactory и без него может быть очень полезен для понимания различий.

Спасибо!

Ответы [ 10 ]

57 голосов
/ 05 июля 2010

Вот один из возможных вариантов использования. Если у вас есть служба executor, которая выполняет ваши выполнимые задачи многопоточным образом, и время от времени ваш поток умирает от необработанного исключения. Давайте предположим, что вы не хотите регистрировать все эти исключения. ThreadFactory решает эту проблему:

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

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

LoggingThreadFactory может быть реализовано так:

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;
    }
}
43 голосов
/ 05 июля 2010

Заводской шаблон - это шаблон творческого проектирования, используемый при разработке программного обеспечения для инкапсуляции процессов, связанных с созданием объектов.

Предположим, у нас есть несколько рабочих потоков для различных задач и мыих со специальными именами (скажем, в целях отладки).Таким образом, мы могли бы реализовать 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++);
   }
}

Если бы у вас было такое требование, было бы довольно сложно реализовать его без фабричного или компоновочного шаблона.


ThreadFactoryявляется частью Java API, потому что он используется и другими классами.Таким образом, приведенный выше пример показывает, почему в некоторых случаях мы должны использовать «фабрику для создания потоков», но, разумеется, абсолютно не нужно реализовывать java.util.concurrent.ThreadFactory для выполнения этой задачи.

18 голосов
/ 19 января 2011

Некоторые внутренние работы

Тема освещена довольно хорошо, за исключением некоторых внутренних работ, которые не так легко увидеть. При создании потока с конструктором вновь созданный поток наследует текущие потоки:

  • ThreadGroup (если не указано или System.getSecurityManager().getThreadGroup() возвращает произвольное ThreadGroup). - Группа потоков сама по себе может быть важной в некоторых случаях и может привести к неправильному завершению / прерыванию потока. ThreadGroup будет использоваться как обработчик исключений по умолчанию.
  • ContextClassLoader - в управляемой среде это не должно быть серьезной проблемой, поскольку среда должна переключать CCL, но если вы хотите это реализовать - имейте это в виду. Утечка CCL вызывающей стороны довольно плохая, так же как и группа потоков (особенно, если threadGroup - некоторый подкласс, а не прямой java.lang.ThreadGroup - необходимо переопределить ThreadGroup.uncaughtException)
  • AccessControlContext - здесь практически ничего не нужно делать (кроме запуска в выделенном потоке), поскольку поле предназначено только для внутреннего использования, и немногие даже подозревают о существовании.
  • размер стека (обычно он не определен, но он может помочь получить поток с очень узким размером стека, в зависимости от вызывающей стороны)
  • приоритет - большинство людей знают о нем и склонны устанавливать его (более или менее)
  • статус демона - обычно это не очень важно и легко заметно (если приложение просто исчезает)
  • Наконец: поток наследует InheritableThreadLocal вызывающего - что может (или не может) приводить к некоторым последствиям. Опять же, ничего не поделаешь, кроме порождения потока в выделенный поток.

В зависимости от применения вышеприведенные пункты могут вообще не оказывать влияния, но в некоторых случаях некоторые из них могут привести к утечкам класса / ресурсов, которые трудно обнаружить и которые проявляют недетерминированное поведение.


Это сделало бы очень длинный пост, но так ...

ниже приведен некоторый (надеюсь) повторно используемый код для реализации ThreadFactory, его можно использовать в управляемых средах для обеспечения правильного ThreadGroup (который может ограничивать приоритет или прерывать потоки), ContextClassLoader, размер стека и т. д. установлены (и / или могут быть настроены) и не просочились. Если есть какой-то интерес, я могу показать, как поступить с унаследованным ThreadLocals или унаследованным акк (который, по сути, может утечь вызывающий 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;
    }
}

Также очень простое использование:

ThreadFactory factory = new TreadFactoryX().priority(3).stackSize(0).wrapRunnable(false).pattern("Socket workers", true).
daemon(false).finishConfig();
4 голосов
/ 06 июля 2010

ИМХО, единственная самая важная функция ThreadFactory - называть потоки чем-то полезным.Наличие потоков в трассировке стека с именем pool-1-thread-2 или хуже Thread-12 является полной проблемой при диагностике проблем.

Конечно, наличие ThreadGroup, статуса демона и приоритета также полезно.

2 голосов
/ 16 января 2016

Использование ThreadFactory в Java

Объект, который создает новые потоки по требованию.Использование потоковых фабрик устраняет жесткую привязку вызовов к new Thread, позволяя приложениям использовать специальные подклассы потоков, приоритеты и т. Д.

Простейшая реализация этого интерфейса - просто:

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

DefaultThreadFactory из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;
        }
    }

Источник

2 голосов
/ 12 сентября 2014

Рекомендуется использовать фабрику пользовательских потоков.Фабрики по умолчанию не очень полезны.Вы должны использовать собственную фабрику по следующим причинам:

  1. Чтобы иметь пользовательские имена потоков
  2. Для выбора между типами потоков
  3. Чтобы выбрать приоритет потока
  4. Для обработки неисследованных исключений

Проверьте этот пост: http://wilddiary.com/understanding-java-threadfactory-creating-custom-thread-factories/

2 голосов
/ 05 июля 2010

Как упомянуто "InsertNickHere", вам нужно понять Factory Pattern .

Хорошим примером использования ThreadFactory является ThreadPoolExecutor : Исполнитель при необходимости создаст нити и позаботится о пуле.Если вы хотите вмешаться в процесс создания и дать специальные имена созданным потокам или назначить их в группу ThreadGroup, вы можете создать ThreadFactory для этой цели и передать его исполнителю.

Это немногобит IoC в стиле.

1 голос
/ 25 января 2015

ThreadFactory - это интерфейс с одним методом public abstract java.lang.Thread newThread(java.lang.Runnable arg0);

Его использование зависит от ваших требований. Предположим, вы хотите, чтобы определенные функции всегда создавали потоки Daemon. Вы можете легко достичь этого с ThreadFactory.

Приведенный ниже код предназначен только для объяснения основ. Это не делает какой-либо конкретной функциональности.

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());
        }
    }
}
0 голосов
/ 10 августа 2017

ThreadFactory будет полезно

  • для установки более описательного имени потока
  • для установки статуса демона потока
  • для установки приоритета потока

Вы можете использовать ThreadFactoryBuilder из Google Guava lib для создания ThreadFactory, подобного этому

ThreadFactory threadFactory = new ThreadFactoryBuilder()
        .setNameFormat("MyThreadPool-Worker-%d")
        .setDaemon(true)
        .build();
0 голосов
/ 02 января 2013

Взгляните на VerboseThreads (агрегаты ThreadFactory) из jcabi-log . Эта реализация заставляет Thread регистрировать исключения, когда они выбрасываются из них. Очень полезный класс, когда вам нужно увидеть, когда и почему ваши темы умирают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...