Именование потоков и пулов потоков ExecutorService



Допустим у меня есть приложение, которое использует Executor база как таковая



Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}


когда я запускаю это приложение в отладчике, создается поток со следующим именем (по умолчанию):Thread[pool-1-thread-1]. Как вы видите, это не очень полезно и, насколько я могу судить,Executor framework не предоставляет простой способ назвать созданные потоки или пулы потоков.



Итак, как можно предоставить имена для потоков/пулов потоков? Например, Thread[FooPool-FooThread].

8914   14  

14 ответов:

вы можете поставить ThreadFactory до newSingleThreadScheduledExecutor(ThreadFactory threadFactory). Фабрика будет отвечать за создание потоков и сможет их называть.

цитировать документация:

создание новой темы

новые потоки создаются с помощью ThreadFactory. Если не указано иное,Executors.defaultThreadFactory() используется, что создает потоки, чтобы все быть в том же ThreadGroup и с теми же NORM_PRIORITY приоритет и не-демон статус. Путем предоставления различных ThreadFactory, вы можете изменить имя потока, группу потоков, приоритет, статус демона и т. д. Если a ThreadFactory не удается создать поток при запросе, возвращая null из newThread, исполнитель продолжит, но не сможет выполнить никаких задач

гуава почти всегда имеет то, что вы нужно.

ThreadFactory namedThreadFactory = 
  new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()

и передайте его своему ExecutorService.

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

class YourThreadFactory implements ThreadFactory {
   public Thread newThread(Runnable r) {
     return new Thread(r, "Your name");
   }
 }

Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);

вы также можете изменить имя вашего потока после этого, в то время как поток выполняется:

Thread.currentThread().setName("FooName");

Это может быть интересно, если, например, вы используете один и тот же ThreadFactory для разных типов задач.

The BasicThreadFactory от apache commons-lang также полезно предоставить поведение именования. Вместо того, чтобы писать анонимный внутренний класс, вы можете использовать конструктор для именования потоков по своему усмотрению. Вот пример из 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);

здесь открыть RFE для этого с Oracle. Из комментариев сотрудника Oracle кажется, что они не понимают проблему и не будут исправлять. Это одна из тех вещей, которые мертво просто поддерживать в JDK (без нарушения обратной совместимости), так что это своего рода позор, что RFE получает неверное понимание.

как указано, вам нужно реализовать свой собственный ThreadFactory. Если вы не хотите тянуть в гуава или Apache Commons только для эту цель я предоставляю здесь ThreadFactory реализация, которую вы можете использовать. Это точно похоже на то, что вы получаете от JDK, за исключением возможности установить префикс имени потока на что-то другое, чем "пул".

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

когда вы хотите использовать его, вам просто воспользоваться тем, что все Executors методы позволяют вам предоставить свой собственный ThreadFactory.

этой

    Executors.newSingleThreadExecutor();

даст ExecutorService, где потоки называются pool-N-thread-M но с помощью

    Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc");

вы получите ExecutorService, где потоки называются primecalc-N-thread-M. Вуаля!

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

пример:

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

}

передайте ThreadFactory в executorservice, и вы хорошо пойдете

быстрый и грязный способ использовать Thread.currentThread().setName(myName); на run() метод.

расширения ThreadFactory

public interface ThreadFactory

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

Thread newThread(Runnable r)

создает новый поток. Реализации могут также инициализировать приоритет, имя, демон статус, группу threadgroup и т. д.

пример кода:

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

выход:

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

....и т. д.

Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());

Runnable getJob() {
        return () -> {
            // your job
        };
}

вы можете написать свою собственную реализацию ThreadFactory, используя, например, некоторые существующие реализации (например, defaultThreadFactory) и изменить имя в конце.

пример реализации 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;
    }
}

и использование:

Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
        Executors.defaultThreadFactory(),
        "customName")
    );

Это моя подгонянная фабрика обеспечивая подгонянные имена для анализаторов сброса потока. Обычно я просто даю tf=null для повторного использования фабрики резьбы по умолчанию JVM. этот вебсайт имеет более предварительную фабрику потока.

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") );

для вашего удобства это цикл дампа потока для отладки цели.

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

Я использую, чтобы сделать то же самое, как показано ниже :

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);

Comments

    Ничего не найдено.