37

我正在使用 ExecutorService 线程池执行一个可调用对象。我想给这个线程起个名字。

更具体地说,在旧版本中我这样做了 -

Thread thread = new Thread(runnable Task);
thread.setName("My Thread Name");

我在 log4j 日志记录中使用线程名称,这对故障排除有很大帮助。现在我正在将我的代码从 Java 1.4 迁移到 Java 1.6。我已经写了这个(如下所示) - 但我不知道如何给这个线程命名。

private final ExecutorService executorPool = Executors.newCachedThreadPool();
Future<String> result = executorPool.submit(callable Task);

请给我一些想法来给这个线程命名?

4

8 回答 8

44

我目前正在这样做:

    someExecutor.execute(new Runnable() {
        @Override public void run() {
            final String orgName = Thread.currentThread().getName();
            Thread.currentThread().setName(orgName + " - My custom name here");
            try {
                myAction();
            } finally {
                Thread.currentThread().setName(orgName);
            }
        }
    });
于 2011-02-28T13:42:35.020 回答
29

您可以使用重载方法:

java.util.concurrent.Executors.newCachedThreadPool(ThreadFactory)

这使您可以通过

java.util.concurrent.ThreadFactory

这应该允许您通过以下方式设置线程的名称java.util.concurrent.ThreadFactory.newThread(Runnable)

构造一个新的Thread. 实现还可以初始化优先级、名称、守护进程状态ThreadGroup等。

查看java.util.concurrent.Executors.DefaultThreadFactory默认实现。

附录

因为我看到这个线程仍然被访问,所以 Guava(如果你有它可用)提供了一个 ThreadFactoryBuilder,它利用了内部匿名类的需要,甚至允许为你的线程自定义参数化名称。

于 2010-07-12T09:07:38.517 回答
10
/**
 * The default thread factory
 */
static class DefaultThreadFactory implements ThreadFactory {
    static final AtomicInteger poolNumber = new AtomicInteger(1);
    final ThreadGroup group;
    final AtomicInteger threadNumber = new AtomicInteger(1);
    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;
    }
}

这是 Java 1.5.0 中的原始实现。因此,您实际上具有 pool-x-thread 的名称,其中 x 代表当前线程组中线程的顺序。

于 2011-01-03T22:15:01.553 回答
8

当您的线程由线程池管理时,重命名线程时应该小心,因为它们实际上被一遍又一遍地重复用于不同的任务,并且一旦重命名,您可能会发现日志中的线程名称没有任何意义...为了避免这种不需​​要的行为,您应该确保在 Runnable/Callable 完成后恢复线程名称。

实现这一点的一种方法是将线程池执行的每个 Runnable/Callable 包装在一个装饰器中,该装饰器处理所有需要的清理。

于 2010-07-12T18:06:23.647 回答
7

如果您使用的是 Spring,则可以为此使用org.springframework.scheduling.concurrent.CustomizableThreadFactory

ExecutorService executorService = Executors.newCachedThreadPool(
                             new CustomizableThreadFactory("MyThreadNamePrefix"));
于 2014-02-16T09:04:45.520 回答
3

我只需要按照您的要求做 - 为我的线程组提供名称 - 所以感谢 Andeas 的回答,我创建了自己的 ThreadFactory 作为将通过复制 Executors$DefaultThreadFactory 来使用它的类的内部类,通过将参数“groupname”添加到构造函数来更改 3 行。

    /**
 * A thread factory that names each thread with the provided group name.
 * <p>
 * Except lines with "elarson modified", copied from code 
 * Executors$DefaultThreadFactory -- Doug Lea, Copyright Oracle, et al.
 */
static class GroupNameThreadFactory implements ThreadFactory {
    private String groupname; /* elarson modified */
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    GroupNameThreadFactory(String groupname) {
        this.groupname = groupname; /* elarson modified */
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                       this.groupname + /* elarson modified */
                       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;
    }
}

从它创建一个 ThreadPool 如下所示。

        ThreadFactory threadfactory = new GroupNameThreadFactory("Monitor");    
    executor = Executors.newFixedThreadPool(NUM_THREADS, threadfactory);

感谢 user381878 提出问题,并感谢 Andreas 的回答。

于 2014-01-20T16:41:26.103 回答
0

只需创建一个匿名 ThreadFactory 即可获取 Executor Service。ThreadFactory 可以为线程提供如下名称:“Thread A”或“Thread B”(参见示例)。

然后使用不同的执行器服务调用可调用任务:

示例类:

公共类 ThreadTester {

public static void main(String[] args) {
    ThreadTester threadTester = new ThreadTester();
    threadTester.test();
}

private void test() {

    ExecutorService executorservice = Executors.newCachedThreadPool(new ThreadFactory() {

        @Override
        public Thread newThread(Runnable arg0) {
            return new Thread(arg0,"Thread A");
        }
    });

    CallableTask taskA = new CallableTask();
    executorservice.submit(taskA);

    executorservice = Executors.newCachedThreadPool(new ThreadFactory() {

        @Override
        public Thread newThread(Runnable arg0) {
            return new Thread(arg0,"Thread B");
        }
    });
    CallableTask taskB = new CallableTask();
    executorservice.submit(taskB);
}

}

可调用任务:

公共类 CallableTask 实现 Callable {

@Override
public Integer call() throws Exception {
    int sum = 0;

    for(int count=1;count<=30;count++){
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " Count :"+count);
        sum = sum + count;
    }
    System.out.println(Thread.currentThread().getName() + " Sum :"+sum);
    return sum;
}

}

于 2016-06-08T09:10:23.280 回答
0

我使用过的一种方法,在我的情况下,我有许多期货并行获取由单个类控制的数据。我提交了一些工作并获得了期货。我将未来存储在地图中,并将值作为作业名称。然后稍后,当我在所有期货上进行超时时,我会从地图中获得工作的名称。这可能不是最灵活的方式,但在我的情况下有效。

于 2016-06-09T14:21:04.187 回答