【问题标题】:With ThreadPoolExecutor, how to get the name of the thread running in the thread pool?使用ThreadPoolExecutor,如何获取线程池中运行的线程名称?
【发布时间】:2012-01-21 08:20:18
【问题描述】:

我在 Java 中使用ThreadPoolExecutor 来管理大量正在运行的线程。我创建了自己的简单ThreadFactory,以便给线程起更好的名称。

问题是线程池第一次创建时在线程中设置了名称,并且与线程池实际运行的任务无关。我理解这一点……我的 Runnables 和 Callables——尽管它们有名字——实际上是 ThreadPoolExecutor 的运行线程的一个抽象层次。

StackOverflow 上还有其他一些关于为ThreadPoolExecutor 线程池创建名称的问题。 (见How to give name to a callable Thread?How to name the threads of a thread pool in Java。)

我想知道的是:有没有人有一个好的解决方案来保持线程池线程的名称与它实际运行的 Runnable 同步?

即如果我调用 Thread.getCurrentThread().getName() 我希望它返回顶级线程池的名称,而是返回线程当前正在运行的 Callable/Runnable 的名称。

由于这主要用于调试和记录目的,我试图避免涉及我将新代码放入每个可能提交给 ThreadPoolExecutor 的 Runnable 的解决方案——我宁愿只是将一些代码放入 ThreadFactory或包装 ThreadPoolExecutor 本身,以便在一个地方完成更改。如果不存在这样的解决方案,我可能不会打扰,因为它不是关键任务。

开始编辑澄清一下,我知道我可以将 Thread.currentThread().setName( "my runnable name" ); 作为每个 Runnable 的 run 方法的第一行,但我试图避免这样做.我在这里是一个完美主义者,我意识到这一点,所以如果人们想评论这个问题并告诉我,我不会被冒犯。 结束编辑

我想,我的另一个问题是人们是否认为做这样的事情是个坏主意。我应该警惕这样更新线程池名称吗?

感谢您的任何建议!

【问题讨论】:

  • "保持线程池线程的名称与它实际运行的 Runnable 同步" --> Runnable / Callable 没有定义名称,所以我不太明白你的问题。您的 ThreadFactory 是否实际上给每个线程一个不同的名称?
  • 对,我的每个 Runnables 和 Callables 都实现了一个命名接口,所以我确实可以访问名称。我意识到我很挑剔,因为我已经为我的所有 Runnables 实现了一个特殊功能,但希望避免添加手动设置线程名称的额外代码。
  • 为清晰起见进行了编辑,将一些“线程池”更改为“线程”,将一些“线程”更改为“任务”。

标签: java multithreading threadpool


【解决方案1】:

创建一个覆盖 beforeExecute 方法的 ThreadPoolExecutor。

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) { }

编辑 TPE 会将 Runnable 包装在 FutureTask 中,因此要支持 toString 方法,您可以覆盖 newTaskFor 并创建自己的包装 FutureTask。

【讨论】:

  • 我现在实际上正在尝试这样的事情,这是个好建议。唯一的问题是当 Runnable 完成时,线程池仍然具有该名称。不幸的是,可覆盖的 afterExecute 方法没有得到运行 Runnable 的线程。完成后我也希望能够将其清理干净。
  • @JeffGoldberg 在 afterExecute 中,Thread.currentThread() 实际上是我在 beforeExecute 中设置名称的线程。我将更新我的答案来证明这一点。
  • 太棒了,这太棒了。谢谢!
  • 没问题!确保您使用的是ConcurrentMap :) 否则您可能会陷入无限循环! mailinator.blogspot.com/2009/06/beautiful-race-condition.html
  • @Zitrax 你说得对,TPE 会将 Runnable 包装在 FutureTask 中。我将更新我的答案以支持toString
【解决方案2】:

所以,我有一个解决方案,既可以设置名称,也可以在名称后进行清理。感谢 Peter Lawrey 和 John Vint 的建议让我来到这里。由于这两个建议都没有完全解决我的问题,我想我会将此示例代码作为单独的答案发布。如果这是不礼貌的行为,我深表歉意——如果是,请告诉我,我会调整的。

在下面的代码中,我决定保留原始 ThreadPoolExecutor 线程的名称并附加 Runnable 名称,然后在 finally 块中删除 Runnable 名称以进行清理,但这很容易改变。

正如 John Vint 所建议的,我更愿意重写 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);
            }
        }
    }
}

【讨论】:

  • 你有更多的工作代码吗?比如getRunnableName()定义和示例调用代码?
【解决方案3】:

我的建议是尝试

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

如果您愿意,也可以在完成后重新设置名称。

【讨论】:

  • 您是建议我每次调用 pool.execute 时都这样做,还是建议我重写池的执行方法?
  • 假设您想更改名称以反映该线程正在执行的操作,是的。注意:它会在 Runnable 启动时更改,而不是在您调用调用时更改。 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-18
  • 2015-05-10
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 2012-03-11
  • 2016-06-04
相关资源
最近更新 更多