熟悉Java并发编程的同学都习惯使用Thread.currentThread().getId();来获取当前线程的ID。但是这个线程ID是否是操作系统的真实线程标识符呢?
1.从一个简单的案例说起
public class Demo {
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getId() + "," + Thread.currentThread().getName() + "=>" + i);
Thread.sleep(5000);
}
}
}
我们知道main方法也是一个特殊的线程,因此,我们把上面的例子,同时并行启动两次,输出结果如下:

我们发现两个不同的线程,它们的线程ID和线程名字居然完全相同!这就说明Thread.currentThread().getId();获得的并不是真正的线程ID,很有可能是JVM内部动态分配的ID。
2.如何获取真实线程ID?
Java语言出于安全性考虑,不允许用户程序获得操作系统真实的线程ID。但是不要忘记Java本身有强大JNI(Java Native Interface),这就为我们解决这个问题,提供了可行的思路。
我们可以尝试用Java去调用C编译的动态链接库,这个动态链接库去调用操作系统获取线程ID的API方法。
实现步骤如下:
1).定义本地方法接口类。
public class GetThreadID {
public static native int getTid();
static {
//这里将来去加载名字叫GetThreadID.dll动态链接库
System.loadLibrary("GetThreadID");
}
}
2).生成头文件
javac GetThreadID.java
javah GetThreadID
GetThreadID.h 头文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class GetThreadID */
#ifndef _Included_GetThreadID
#define _Included_GetThreadID
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: GetThreadID
* Method: getTid
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_GetThreadID_getTid
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
3).创建动态链接库工程
大家选择自己熟悉的C或者C++ 的IDE工具(作者这里使用了DevC++),创建一个DLL工程,项目结构图如下:

其中的jni.h和jni_md.h,大家在自己安装的JDK的include和include/win32下寻找,并且把它们复制到工程目录里面。
GetThreadID.c 文件内容如下:
#include "jni.h"
#include "windows.h"
#include <unistd.h>
#include "GetThreadID.h"
JNIEXPORT jint JNICALL Java_GetThreadID_getTid(JNIEnv *env, jobject obj) {
DWORD tid = GetCurrentThreadId();
return tid;
}
这里要特别注意:头文件里声明的方法名字一定要和源程序中定义的方法名字完全相同。其中的GetCurrentThreadId();方法就是windows获取线程ID的系统API。
4).编译生成动态链接库

查看是否已经生成了动态链接库。

把GetThreadID.dll文件复制到C:\Windows\System32 目录下。如下图所示:

5).测试执行。
测试程序改写如下:
public class Demo {
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("Thread Id:"+GetThreadID.getTid()+" =>"+i);
Thread.sleep(5000);
}
}
}
我们把上面的例子再次同时并行启动两次,输出结果如下:

这次我们发现两个不同的线程,具有真正意义上不同的线程ID。为了进一步验证,程序获得就是操作系统真实的线程ID,我们拿程序输出的结果与windows的任务管理器里查看的线程ID进行对比,结果完全一致!

小结: