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

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

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

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

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

Ответ 1

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

Чтобы процитировать Javadoc:

Создание новых потоков

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

Ответ 2

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

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

и передать его вашему ExecutorService.

Ответ 3

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

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

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

Ответ 4

Вы также можете изменить название своего потока после выполнения потока:

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

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

Ответ 5

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

Ответ 6

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

Как уже указывалось, вам нужно реализовать свой собственный ThreadFactory. Если вы не хотите использовать Guava или 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. Вуаля!

Ответ 7

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

Пример:

ExecutorService alphaExecutor =
    Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));

Ответ 8

private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

Передайте ThreadFactory службе-исполнителям, и вам хорошо идти

Ответ 9

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

Ответ 10

Расширить 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

.... и т.д.

Ответ 11

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

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

Ответ 12

Вы можете написать собственную реализацию 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")
    );

Ответ 13

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

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

Ответ 14

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

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

Ответ 15

Самодельное ядро Java-решение, которое я использую для украшения существующих фабрик:

public class ThreadFactoryNameDecorator implements ThreadFactory {
    private final ThreadFactory defaultThreadFactory;
    private final String suffix;

    public ThreadFactoryNameDecorator(String suffix) {
        this(Executors.defaultThreadFactory(), suffix);
    }

    public ThreadFactoryNameDecorator(ThreadFactory threadFactory, String suffix) {
        this.defaultThreadFactory = threadFactory;
        this.suffix = suffix;
    }

    @Override
    public Thread newThread(Runnable task) {
        Thread thread = defaultThreadFactory.newThread(task);
        thread.setName(thread.getName() + "-" + suffix);
        return thread;
    }
}

В бою:

Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));

Ответ 16

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

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));

Ответ 17

Как уже говорилось в других ответах, вы можете создать и использовать собственную реализацию интерфейса java.util.concurrent.ThreadFactory (внешние библиотеки не требуются). Я вставляю свой код ниже, потому что он отличается от предыдущих ответов, поскольку он использует метод String.format и принимает базовое имя для потоков в качестве аргумента конструктора:

import java.util.concurrent.ThreadFactory;

public class NameableThreadFactory implements ThreadFactory{
    private int threadsNum;
    private final String namePattern;

    public NameableThreadFactory(String baseName){
        namePattern = baseName + "-%d";
    }

    @Override
    public Thread newThread(Runnable runnable){
        threadsNum++;
        return new Thread(runnable, String.format(namePattern, threadsNum));
    }    
}

И это пример использования:

ThreadFactory  threadFactory = new NameableThreadFactory("listenerThread");        
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);