HOWTO · Java

在 Java 中获取线程 ID

本文将介绍在 Java 中获取线程 id 的方法。

本页内容

在 Java 中,可以对 Thread 对象调用 Thread.getId(),读取该线程的标识符。任务在 executor 中运行时,Thread.currentThread().getId() 返回正在执行该任务的工作线程 ID。下面的示例分别展示这两种情况;这两个值都不代表线程池或线程名称。已有 Thread 引用时使用第一种方法;需要在任务代码中识别当前工作线程时使用第二种方法。

在 Java 中使用 Thread.getId() 获取线程 ID

创建一个 Thread 对象,然后调用它的 getId() 方法,以 long 类型取得该对象的标识符。第一个示例创建两个线程对象并读取它们各自的 ID。示例不会启动这两个线程;读取这些对象的标识符不需要先启动它们。标识符由运行时分配,在不同运行或环境中可能不同,因此应在运行时使用返回值,而不要依赖示例中的数字。当代码持有某个 Thread 的引用,并需要将它与另一个线程对象区分开时,这种方法很有用。

public class GetThreadID {
  public static void main(String[] args) {
    Thread thread1 = new Thread();
    Thread thread2 = new Thread();

    System.out.println("Thread 1 ID: " + thread1.getId());
    System.out.println("Thread 2 ID: " + thread2.getId());
  }
}

以下是一次运行的示意输出。输出块中的文字标签仍为英文,以保持示例内容不变:

Thread 1 ID: 12
Thread 2 ID: 13

上面的数字 ID 仅为示意,实际值取决于运行时。由于线程并未启动,程序会按照打印语句的顺序输出这两个对象的 ID;在另一次运行或其他环境中,示例中的数字不一定相同。

使用 Thread.currentThread().getId() 获取线程池中当前工作线程的 ID

提交给 executor 的任务会在其中一个工作线程上运行。在任务内部调用 Thread.currentThread().getId(),即可取得当前正在执行该任务的工作线程 ID。这个值是工作线程的 ID,不是 executor 或线程池的标识。固定线程池会重复使用其中的工作线程,因此不同任务可能报告相同的工作线程 ID。由调度决定哪个工作线程处理每项任务,所以任务顺序和数字 ID 都可能变化。排查某项工作由哪个线程执行时,这一区别很重要:返回的数字描述的是当时正在执行任务的工作线程,而不是负责调度任务的 executor。

示例提交五项任务,打印每项任务的编号及其工作线程 ID,然后关闭 executor。shutdown() 会停止接收新任务,同时允许已提交的任务完成。awaitTermination 最长等待一分钟,不会忙等。如果任务耗时超过这段时间,程序会报告等待超时;关闭请求已经发出,因此队列中的任务仍可继续执行。在生产代码中,应选择适合应用工作负载的超时时长,并确定任务未能按时完成时应用如何响应。如果需要针对特定 Java 版本选择 API 细节,请查阅该版本的文档;这里未指定目标版本。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class GetThreadID {
  public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    for (int i = 1; i <= 5; i++) {
      final int taskNumber = i;
      executor.execute(() -> {
        long workerId = Thread.currentThread().getId();
        System.out.println("Task " + taskNumber + " ran on worker ID " + workerId);
      });
    }

    executor.shutdown();
    boolean finished = executor.awaitTermination(1, TimeUnit.MINUTES);
    System.out.println(finished ? "All tasks finished" : "Wait timed out");
  }
}

以下是一次运行的示意输出;任务顺序和数字 ID 取决于运行时,输出块中的文字标签仍为英文,以保持示例内容不变:

Task 2 ran on worker ID 13
Task 3 ran on worker ID 13
Task 4 ran on worker ID 13
Task 5 ran on worker ID 13
Task 1 ran on worker ID 12
All tasks finished

如果要识别执行某项任务的工作线程,请在任务内部调用该方法。在其他位置调用时,返回的是运行那段代码的线程 ID。线程池可能重复使用工作线程,任务调度也可能不同,因此应将输出用于了解某一次运行,而不要把任务编号与工作线程 ID 的对应关系视为固定不变。