背景
pthread 库中有一个 pthread_self() 接口用来获取线程 ID,但是这个 ID并不是内核中那个线程 ID,pthread_t 到底是个什么样的数据结构呢?因为 POSIX 标准并没有限制 pthread_t 的数据类型,所以该类型取决于具体实现。对于 Linux 目前使用的 NPTL 实现而言,pthread_t 类型的线程 ID,本质上就是一个进程地址空间上的一个地址,而且 pthread_t 类型的线程 ID很有可能被复用。进程之间不会存在重复的线程 ID,而且不同线程之间也不会重复,在任意时刻都是全局唯一的值。
来源:在学习偏向锁的过程中,偏向锁是不能重新偏向的,为什么在各个线程中发现都是偏向到同一个线程?
上一个线程拿到锁之后,是偏向锁。现在一个新线程拿到锁之后,可能复用了上一个ID。JVM判断加锁的对象是可偏向的,进一步对比发现偏向的线程还是当前线程,所以偏向锁使用成功。实际上这是一个新线程了。其实这样加上偏向锁性能还好些,免得升级锁。
证明
JNI native code 获取线程ID
JNIEXPORT void JNICALL Java_jnitool_JniTool_getThreadID(JNIEnv * env, jobject obj){fprintf(stdout,"java_lock log PID = %d TID = %lu \n",getpid(),pthread_self());}
测试代码,让线程顺序执行。
public static void main(String[] args) throws Exception {out.println("\n main ------------------------ start");JniTool jniTool = new JniTool();Thread thread1 = new Thread(new Runnable() {public void run() {out.println("t1------------------------ start");jniTool.getThreadID();out.println("java getId "+(Thread.currentThread().getId()));out.println("t1------------------------ end");}});thread1.start();thread1.join();Thread thread2 = new Thread(new Runnable() {public void run() {out.println("t2------------------------start");jniTool.getThreadID();out.println("java getId "+(Thread.currentThread().getId()));out.println("t2------------------------end");}});thread2.start();thread2.join();Thread thread3 = new Thread(new Runnable() {public void run() {out.println("t3------------------------start");jniTool.getThreadID();out.println("java getId "+(Thread.currentThread().getId()));out.println("t3------------------------end");}});thread3.start();thread3.join();out.println("\n main ------------------------ end");}}
main ------------------------ startt1------------------------ startjava_lock log PID = 55 TID = 140073151424256java getId 8t1------------------------ endt2------------------------startjava_lock log PID = 55 TID = 140073151424256java getId 9t2------------------------endt3------------------------startjava_lock log PID = 55 TID = 140073151424256java getId 10t3------------------------endmain ------------------------ end
观察输出,查看线程ID 确实被复用了。
