С ThreadPoolExecutor, как получить имя потока, работающего в пуле потоков? - PullRequest
10 голосов
/ 15 декабря 2011

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

Проблема заключается в том, что имя задается в потоке при первом создании пула потоков и не привязано к задаче, которую фактически выполняет пул потоков. Я понимаю это ... мои Runnables и Callables - хотя у них есть имена - на самом деле являются одним уровнем абстракции по сравнению с запущенными потоками ThreadPoolExecutor.

В StackOverflow были и другие вопросы о создании имен для ThreadPoolExecutor пулов потоков. (См. Как дать имя вызываемому потоку? и Как назвать потоки пула потоков в Java .)

Что я хочу знать: есть ли у кого-нибудь хорошее решение для синхронизации имени потока пула в синхронизации с Runnable, в котором он фактически работает?

т.е. Если я вызову Thread.getCurrentThread().getName(), я бы хотел, чтобы не возвращал имя пула потоков верхнего уровня, а скорее имя Callable / Runnable, которое поток в данный момент выполняет.

Поскольку это главным образом для целей отладки и ведения журнала, я стараюсь избегать решения, которое включает в себя добавление нового кода в каждый Runnable, который может быть передан в ThreadPoolExecutor - я бы предпочел просто поместить некоторый код в ThreadFactory или оберните сам ThreadPoolExecutor, чтобы изменение выполнялось в одном месте. Если такого решения не существует, я, вероятно, не буду беспокоиться, поскольку оно не имеет решающего значения.

начать редактирование Чтобы уточнить, я знаю, что могу поставить Thread.currentThread().setName( "my runnable name" ); в качестве первой строки метода run каждого Runnable, но я пытаюсь избежать этого. Я здесь перфекционист, и я это понимаю, поэтому я не буду обижаться, если люди захотят прокомментировать этот вопрос и скажут мне об этом. конец редактирования

Мой другой вопрос, я полагаю, заключается в том, считают ли люди плохой идеей делать такие вещи. Стоит ли с осторожностью обновлять имя пула потоков, как это?

Спасибо за любые предложения!

Ответы [ 3 ]

15 голосов
/ 15 декабря 2011

Создайте ThreadPoolExecutor, который переопределяет метод beforeExecute.

private final ThreadPoolExecutor executor = new ThreadPoolExecutor (new ThreadPoolExecutor(10, 10,  0L, TimeUnit.MILLISECONDS,  new LinkedBlockingQueue<Runnable>()){   
    protected void beforeExecute(Thread t, Runnable r) { 
         t.setName(deriveRunnableName(r));
    }

    protected void afterExecute(Runnable r, Throwable t) { 
         Thread.currentThread().setName("");
    } 

    protected <V> RunnableFuture<V> newTaskFor(final Runnable runnable, V v) {
         return new FutureTask<V>(runnable, v) {
             public String toString() {
                return runnable.toString();
             }
         };
     };
}

Не уверен, как точно будет работать derveRunnableName(), возможно toString()?

Редактировать: Thread.currentThread ()на самом деле поток устанавливается в beforeExecute, который вызывает afterExecute.Вы можете сослаться на Thread.currentThread (), а затем установить имя в afterExecute.Это отмечено в javadocs

/**
 * Method invoked upon completion of execution of the given Runnable.
 * This method is invoked by the thread that executed the task. If
 * non-null, the Throwable is the uncaught <tt>RuntimeException</tt>
 * or <tt>Error</tt> that caused execution to terminate abruptly.
 *
 * <p><b>Note:</b> When actions are enclosed in tasks (such as
 * {@link FutureTask}) either explicitly or via methods such as
 * <tt>submit</tt>, these task objects catch and maintain
 * computational exceptions, and so they do not cause abrupt
 * termination, and the internal exceptions are <em>not</em>
 * passed to this method.
 *
 * <p>This implementation does nothing, but may be customized in
 * subclasses. Note: To properly nest multiple overridings, subclasses
 * should generally invoke <tt>super.afterExecute</tt> at the
 * beginning of this method.
 *
 * @param r the runnable that has completed.
 * @param t the exception that caused termination, or null if
 * execution completed normally.
 */
protected void afterExecute(Runnable r, Throwable t) { }

Edit TPE обернет Runnable в FutureTask, поэтому для поддержки метода toString вы можете переопределить newTaskFor и создать свой собственныйЗавернутый FutureTask.

4 голосов
/ 15 декабря 2011

Итак, у меня есть решение, которое позволяет как установить имя, так и очистить его после имени. Спасибо и Питеру Лоури, и Джону Винту за их предложения, которые привели меня сюда. Поскольку ни одно из предложений полностью не решило мою проблему, я решил опубликовать этот пример кода в качестве отдельного ответа. Я прошу прощения, если это плохой этикет - если так, дайте мне знать, и я приспособлюсь.

В приведенном ниже коде я решил оставить имя исходного потока ThreadPoolExecutor и добавить имя Runnable, а затем в блоке finally удалить имя Runnable для очистки, но это можно легко изменить.

Как предполагает Джон Винт, я бы предпочел переопределить метод beforeExecution, а затем переопределить метод afterExecution для очистки, но afterExecution не имеет дескриптора потока.

public class RunnableNameThreadPoolExecutor extends ThreadPoolExecutor {

    /* Constructors... */

    @Override
    public void execute(Runnable command) {
        super.execute(new ManageNameRunnable(command));
    }

    private class ManageNameRunnable implements Runnable {
        private final Runnable command;
        ManageNameRunnable( Runnable command ) {
            this.command = command;
        }

        public void run() {
            String originalName = Thread.currentThread().getName();
            try {
                String runnableName = getRunnableName(command);
                Thread.currentThread().setName(originalName+ ": " + runnableName);
                command.run();
            } finally {
                Thread.currentThread().setName(originalName);
            }
        }
    }
}
2 голосов
/ 15 декабря 2011

Мое предложение было бы попробовать

pool.execute(new Runnable() {
    public void run() {
         Thread.getCurrentThread().setName("My descriptive Runnable");
         // do my descriptive Runnable
    }
});                 

Вы также можете сбросить имя, когда закончите, если хотите.

...