首页 > 技术文章 > 0.并发编程-openjdk编译调试、java线程模型

tangliMeiMei 2021-09-23 11:04 原文

解决问题:java当中的线程和操作系统的线程是什么关系?

关于操作系统的线程

1.linux操作系统的线程控制原语

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

2.linux内核函数之pthread_create

在这里插入图片描述

根据man配置的信息:

参数 解释 备注
pthread_t *thread 传出参数,调用之后会传出被创建线程的id 定义pthread_t pid;继而取地址 &pid
const pthread_attr_t *attr 线程属性,关于线程属性是Linux的知识 在学习pthread_create函数的时候一般传NULL,保持默认属性
void (start_routine)(void *) 线程的启动后的主体函数相当于java当中的run 需要你定义一个函数,然后传函数名即可
void *arg 主体函数的参数 如果没有可以传NULL

3.创建一个os线程

实验步骤;

1.在opt/local/cCode/thread 目录下

2.新增一个thread.c文件 cat>>thread.c

#include <pthread.h>// //导入对应的头文件 
#include <stdio.h>
pthread_t pid;//定义一个变量,接受创建线程后的线程id
//定义线程的主体函数
void* thread_entity(void* arg)
{   
    while(1)
    {   //线程睡眠
       usleep(100);
      printf("i am new Thread!");  
    }
    
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
    //调用操作系统的函数创建线程,注意四个参数
    pthread_create(&pid,NULL,thread_entity,NULL);
    //usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?
    //为什么需要睡眠?如果不睡眠会出现什么情况
    while(1)
    {
        usleep(100);
        printf("main\n");
    }
    return 0;
}

在这里插入图片描述

4.gcc -o xx thread.c -pthread 编译c文件

ps: 编译的时候报错 如下所示:

img

报错解决:增加头文件 unistd.h http://www.voidcn.com/article/p-nyrseuhh-da.html https://stackoverflow.com/questions/29370813/implicit-declaration-of-function-usleep

修改过后的代码

#include <pthread.h>//头文件
#include <stdio.h>
#include< unistd.h>
pthread_t pid;//定义一个变量,接受创建线程后的线程id
//定义线程的主体函数
void* thread_entity(void* arg)
{   
    while(1)
    { 
       usleep(100);
      printf("i am new Thread!");  
    }
    
}
//main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
int main()
{
    //调用操作系统的函数创建线程,注意四个参数
    pthread_create(&pid,NULL,thread_entity,NULL);
    //usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?
    //为什么需要睡眠?如果不睡眠会出现什么情况
    while(1)
    {
        usleep(100);
        printf("main\n");
    }
    return 0;
}

img

5.gcc -o xx thread.c -pthread 编译成功

6.执行 ./xx

 ./xx

输出结果:main线程跟 使用os函数pthread_create 创建的线程交替执行

img

关于Java线程

假设有了上面知识的铺垫,那么可以试想一下java的线程模型到底是什么情况呢?

在java代码里启动一个线程的代码

public class Example4Start {

    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("i am new Thread!");
            }
        }; 
        thread.start();
    }
}

问题:这里启动的线程和上面我们通过linux的pthread_create函数启动的线程有什么关系呢?

只能去可以查看start()的源码了, 看看java的start()到底干了什么事才能对比出来.

start源码的部分截图,可以看到这个方法最核心的就是调用了一个start0方法,而start0方法又是一个native方法,

img

img

即:start()->start0 [native方法】

故而如果要搞明白start0我们需要查看Hotspot的源码,好吧那我们就来看一下Hotspot的源码吧,Hotspot的源码怎么看么?

一般直接看openjdk的源码,openjdk的源码如何查看、编译调试?openjdk的编译我们后面会讨论,

在没有openjdk的情况下,我们做一个大胆的猜测:

java级别的线程其实就是操作系统级别的线程,什么意思呢?说白了我们大胆猜想:

java 中start方法-》start0本地方法-》调用os库函数:ptherad_create创建线程

我们鉴于这个猜想来模拟实现一下。

验证猜想demo:

1.LubanThread.java

public class LubanThread {
    public static void main(String[] args) {
        LubanThread lubanThread =new LubanThread();
        lubanThread.start0();
    }
    private native void start0();
}

这里我们让自己写的start0调用一个本地方法,在本地方法里面去启动一个系统线程,好吧我们写一个c程序来启动本地线程

#include <pthread.h>
#include <stdio.h>
#include< unistd.h>
pthread_t pid;
void* thread_entity(void* arg)
{   
    while(1){
		usleep(100);
    	printf("I am new Thread\n");
    }
}
void* start(){
    pthread_create(&pid,NULL,thread_entity,NULL);
    while(1){
		usleep(100);
		printf("I am  main\n");
	}
    
}
int main()
{
  start();
  return 0;
}

在linux上编译、运行上述C程序

gcc thread.c -o thread.out -pthread
./thread.out 

img

结果是两个线程一直在交替执行,得到我们预期的结果。

现在的问题就是我们如何通过start0调用这个c程序,这里就要用到JNI了,(后面不jni吧。我不知道jni是啥?)

1.修改LubanThread .java文件

LubanThread .java

public class LubanThread {
    static {
        System.loadLibrary( "LubanThreadNative" );
    }
    public static void main(String[] args) {
        LubanThread lubanThread =new LubanThread();
        lubanThread.start0();
    }
    private native void start0(); // 调用上面c的start();
}

2.编译LubanThread.java 生产成 LubanThread.class字节码文件

javac LubanThread.java

3.生成LubanThread.h 头文件

javah LubanThread

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class LubanThread */

#ifndef _Included_LubanThread
#define _Included_LubanThread
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     LubanThread
 * Method:    start0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_LubanThread_start0
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
其中JNIEXPORT void JNICALL Java_LubanThread_start0
  (JNIEnv *, jobject);方法就是你需要在C程序中定义的方法。

4.复制一份thread.c 命名为threadnew.c

注意: 引入头文件LubanThread.h

#include <pthread.h>//头文件
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h" //注意引入头文件
pthread_t pid;
void* thread_entity(void* arg)
{
    while(1){
                usleep(100);
        printf("I am new Thread\n");
    }
}

/*
 * Class:     com_luban_concurrency_LubanThread
 * Method:    start0
 * Signature: ()V
*/
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0
  (JNIEnv * env, jobject c1){
    pthread_create(&pid,NULL,thread_entity,NULL);
    while(1){
        usleep(100);
        printf("I am  main\n");
   }


  }


int main()
{
//  start();
  return 0;
}


5.生成libLubanThreadNative.so 文件

gcc  -fPIC -I  /usr/lib/jdk/jdk1.8.0_162/include  -I /usr/lib/jdk/jdk1.8.0_162/include/linux    -shared -o libLubanThreadNative.so threadnew.c

解析类,把这个threadnew.c编译成为一个动态链接库,这样在java代码里会被laod到内存 libLubanThreadNative这个命名需要注意libxx,xx就等于你java那边写的字符串

Snipaste_2021-09-22_17-13-16

注意:/usr/lib/jvm/java-1.8.0-openjdk/include需要修改成linux机器上jdk的位置:

使用:echo $JAVA_HOME 查看jdk安装位置 : 我的是 /usr/lib/jdk/jdk1.8.0_162

6.libLubanThreadNative.so 文件加入到path

做完这一系列事情之后需要把这个.so文件加入到path,这样java才能load到

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libLubanThreadNative.so}所在的路径

{libLubanThreadNative.so} 我的是 export LD_LIBRARY_PATH=/opt/local/cCode/thread

export LD_LIBRARY_PATH=/opt/local/cCode/thread

7.执行 java LubanThread

万事俱备,直接测试,运行我们自己写的那个java类直接测试看看结果能不能启动线程

打印结果:

img

牛逼!我们已经通过自己写的一个类,启动了一个线程,但是这个线程函数体是不是java的是C程序的,这个java线程的run方法不同。接下来我们来实现一下这个run方法 (C来调用java的方法,是jni反调用java方法)

猜想demo2:回调Java层的run方法

1.LubanThread.java新增run方法

public class LubanThread {
    static {
        System.loadLibrary( "LubanThreadNative" );
    }
    public static void main(String[] args) {
        LubanThread lubanThread =new LubanThread();
        lubanThread.start0();
    }
    //这个run方法,要让C程序员调用到,就完美了
    public void run(){
        System.out.println("I am java Thread !!");
    }
    private native void start0();
}

让我们思考一下现在的问题是什么?

就是让C程序当中的thread_entity方法调用到java当中的run方法,思路同样是jni反调用java方法

2.修改threadnew.c

现在我们只需要将原来threadnew.c中代码修改,利用JNI反向调用LubanThread类中run方法即可模拟出Java的线程了。当我打开threadnew.c的文件的时候,我傻了,怎么在thread_entity获取到JNIEnv对象?

于是开始我的尝试:

尝试一:利用全局变量将其传出去,很可惜失败了。

下面是错误的代码1

#include <pthread.h>//头文件
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h" //注意引入头文件
pthread_t pid;
JNIEnv * env;
void* thread_entity(void* arg)
{  
    //定一个class 对象
  jclass cls;
  jmethodID cid;
  jmethodID rid;
  //定一个对象
  jobject obj;
  jint ret = 0;
  //通过虚拟机对象找到TestThread java class
  cls = (*env)->FindClass(env,"TestThread");
  if(cls == NULL){
    printf("FindClass Error!\n")
    return;
  }
   //找到LubanThread类中无参的构造函数
  cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
  if (cid == NULL) {
    printf("GetMethodID Error!\n");
    return;
  }
  //实例化一个对象
  obj = (*env)->NewObject(env, cls, cid);
  if(obj == NULL){
    printf("NewObject Error!\n")
    return;
  }
  rid = (*env)->GetMethodID(env, cls, "run", "()V");
  //ret = (*env)->CallIntMethod(env, obj, rid, Null);
  // printf("Finsh call method!\n")
    while(1){
        usleep(100);
        (*env)->CallIntMethod(env, obj, rid, Null);        
    }
}

/*
 * Class:     com_luban_concurrency_LubanThread
 * Method:    start0
 * Signature: ()V
*/
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv * envself, jobject c1){
    env = envself;
    pthread_create(&pid,NULL,thread_entity,NULL);
    while(1){
        usleep(100);
        printf("I am  main\n");
   }

}

int main()
{
//  start();
  return 0;
}


尝试二:利用搜索引擎查找到了c语言中嵌套Java虚拟机,很可惜还是失败了,

错误的代码2:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
void* thread_entity(void* arg){
    int res;
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args;
    JavaVMOption options[3];
    /*设置初始化参数*/
    options[0].optionString = "-Djava.compiler=NONE";
    options[1].optionString = "-Djava.class.path=.";
    options[2].optionString = "-verbose:jni";	//用于跟踪运行时的信息
    /*版本号设置不能漏*/
    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 3;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = JNI_TRUE;
    res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    jclass cls;
    jobject obj;
    jmethodID cid;
    jmethodID rid;
    jint ret = 0;
    //通过虚拟机找到Java的类(LubanThread)
    cls = (*env)->FindClass(env, "LubanThread");
    if (cls == NULL) {
        printf("FindClass error!\n");
    }
    //找到LubanThread类中无参的构造函数
    cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    if (cid == NULL) {
        printf("Query constructor error!\n");
    }
    //实例化对象
    obj = (*env)->NewObject(env, cls, cid);
    if (obj == NULL) {
        printf("NewObject error!\n");
    }
    //查找run方法
    rid = (*env)->GetMethodID(env, cls, "run", "()V");
    if (rid == NULL) {
        printf("Query runMethod error\n");
    }
    while (1) {
        usleep(100);
        //调用run方法
        ret = (*env)->CallIntMethod(env, obj, rid, NULL);
    }
}

// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *env, jobject c1){
    pthread_create(&pid,NULL,thread_entity,NULL);
    while(1){
    	usleep(100);
    	printf("I am main\n");
	}
}
int main(){
	return 0;
}

尝试三:搜出来说我路径写错了,于是又修改了一遍路径,进行尝试,很可惜还是失败了,下面是错误的代码
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
void* thread_entity(void* arg){
    int res;
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args;
    JavaVMOption options[3];
    /*设置初始化参数*/
    options[0].optionString = "-Djava.compiler=NONE";
    options[1].optionString = "-Djava.class.path=/usr/lib/jdk/jdk1.8.0_162/";
    options[2].optionString = "-verbose:jni";	//用于跟踪运行时的信息
    /*版本号设置不能漏*/
    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 3;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = JNI_TRUE;
    res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    jclass cls;
    jobject obj;
    jmethodID cid;
    jmethodID rid;
    jint ret = 0;
    //通过虚拟机找到Java的类(LubanThread)
    cls = (*env)->FindClass(env, "LubanThread");
    if (cls == NULL) {
        printf("FindClass error!\n");
    }
    //找到LubanThread类中无参的构造函数
    cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    if (cid == NULL) {
        printf("Query constructor error!\n");
    }
    //实例化对象
    obj = (*env)->NewObject(env, cls, cid);
    if (obj == NULL) {
        printf("NewObject error!\n");
    }
    //查找run方法
    rid = (*env)->GetMethodID(env, cls, "run", "()V");
    if (rid == NULL) {
        printf("Query runMethod error\n");
    }
    while (1) {
        usleep(100);
        //调用run方法
        ret = (*env)->CallIntMethod(env, obj, rid, NULL);
    }
}

// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *env, jobject c1){
    pthread_create(&pid,NULL,thread_entity,NULL);
    while(1){
    	usleep(100);
    	printf("I am main\n");
	}
}
int main(){
	return 0;
}
尝试四:我突然想到了安卓底层硬件要调用Java的函数,只能在c语言中调用,

它是怎么做的呢?于是搜了一下,最后仿照示例写了一段代码,终于成功了。代码如下

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include "LubanThread.h"//记得导入刚刚编译的那个.h文件
pthread_t pid;
JavaVM *javaVM;
void *thread_entity(void *arg) {
    JNIEnv* env = NULL;
    //从jvm对象中取得JNIEvn对象
    (*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
    jclass cls;
    jobject obj;
    jmethodID cid;
    jmethodID rid;
    jint ret = 0;
    //通过虚拟机找到Java的类(LubanThread)
    cls = (*env)->FindClass(env, "LubanThread");
    if (cls == NULL) {
        printf("FindClass error!\n");
    }
    //找到LubanThread类中无参的构造函数
    cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    if (cid == NULL) {
        printf("Query constructor error!\n");
    }
    //实例化对象
    obj = (*env)->NewObject(env, cls, cid);
    if (obj == NULL) {
        printf("NewObject error!\n");
    }
    //查找run方法
    rid = (*env)->GetMethodID(env, cls, "run", "()V");
    if (rid == NULL) {
        printf("Query runMethod error\n");
    }
    while (1) {
        usleep(100);
        //调用run方法
        ret = (*env)->CallIntMethod(env, obj, rid, NULL);
    }
}
// 注意:这个方法要参考LubanThread.h文件的Java_LubanThread_start0,这里的参数得注意,你写死就行,不用明白为什么
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *oldEnv, jobject c1) {
    pthread_create(&pid, NULL, thread_entity, NULL);
    while (1) {
        usleep(100);
        printf("I am main\n");
    }
}
//jvm启动的时候调用,将jvm对象返回出去
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
    javaVM = vm;c
    return JNI_VERSION_1_8;
}
int main() {
    return 0;
}


3.然后再走一遍生成.class、.h 、so

gcc  -fPIC -I  /usr/lib/jdk/jdk1.8.0_162/include  -I /usr/lib/jdk/jdk1.8.0_162/include/linux    -shared -o libLubanThreadNative.so threadnew.c
此时报错:

```shell
LubanThread.h:2:10: fatal error: jni.h: No such file or directory
    2 | #include <jni.h>
      |          ^~~~~~~
compilation terminated.
```
LubanThread.h:2:10: fatal error: jni.h: No such file or directory
    2 | #include <jni.h>
      |          ^~~~~~~
compilation terminated.

     
原因:

好像是#include "jni_md.h"会将文件包含在与jni.h相同的目录中,但是现在找不到了。

解决办法

在以前的JDK版本中,jni_md.h 似乎和另一个文件都位于include / linux文件夹中,但是include中都有指向这两个文件的符号链接。
于是,先在本地系统查找jni_md.h文件的位置,使用如下命令:

 find / -name jni_md.h 2> /dev/null
在我的系统里,找到jni_md.h的路径为:

/usr/lib/jvm/jdk1.8.0_221/include/linux/jni_md.h
果然在include/linux文件夹下,因此,可以仅创建如下两个文件的符号链接:

$ sudo ln -s /usr/lib/jvm/jdk1.8.0_221/include/linux/jni_md.h /usr/lib/jvm/jdk1.8.0_221/include/jni_md.h
$ sudo ln -s /usr/lib/jvm/jdk1.8.0_221/include/linux/jawt_md.h /usr/lib/jvm/jdk1.8.0_221/include/jawt_md.h
最后顺利解决该问题!
https://www.cnblogs.com/geekHao/p/12817565.html

继续执行:

gcc  -fPIC -I  /usr/lib/jdk/jdk1.8.0_162/include  -I /usr/lib/jdk/jdk1.8.0_162/include/linux    -shared -o libLubanThreadNative.so threadnew.c

此时会有warnning,不用管:

root@ubuntu:/opt/local/cCode/thread2# gcc -o threadnnew threadnew.c -I /usr/lib/jdk/jdk1.8.0_162/include -I /usr/lib/jdk/jdk1.8.0_162/includ/linux  -L /usr/lib/jdk/jdk1.8.0_162/jre/lib/amd64/server -ljvm -pthread
threadnew.c: In function ‘thread_entity’:
threadnew.c:10:43: warning: passing argument 2 of ‘(*javaVM)->AttachCurrentThread’ from incompatible pointer type [-Wincompatible-pointer-types]
   10 |     (*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
      |                                           ^~~~
      |                                           |
      |                                           const struct JNINativeInterface_ ***
threadnew.c:10:43: note: expected ‘void **’ but argument is of type ‘const struct JNINativeInterface_ ***’
root@ubuntu:/opt/local/cCode/thread2# 

4.libLubanThreadNative.so 文件加入到path

export LD_LIBRARY_PATH=/opt/local/cCode/thread2

5.执行 java LubanThread

最后执行,发现已经成功了,可以回调我们Java类中run方法了,至此我们模拟的Java中Thread类已经完成了。
在这里插入图片描述

文件目录

image-20210922220240673

TOdo:

todo1:未编译openjdk 源码,尝试失败。搞了5、6个钟头未成功

未干完的事情,本来想要按照教程看一下,检验一下面的步骤:

1.我们先写一个Java多线程程序,然后将写好的Java程序放到我们的编译好的JDK源码下面build/linux-x86_64-server-fastdebug/jdk/bin/路径下,可能每个人编译路径路径不一样,你要找到你对应的文件下面,不然调试不行。

2.但是苦于我搞了一上午源码编译未成功,也就是build/linux-x86_64-server-fastdebug/jdk/ 下面无法生成bin路径

所以保留下了代码有机会再尝试 ,(应该是没有机会);

Test.java

public class Test{
	public static void main(String[] args){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
            	System.out.println("haha");
            }
        });
        thread.start();
    }
}


执行行javac命令将Test.java文件编译成Test.class文件,

最后我们在Clion中进行调试。调试之前,要进行对应的调试的配置。

在这里插入图片描述

在这里插入图片描述

当断点和调试配置弄好过后,我们可以开始debug了,来验证我们的猜想。

在这里插入图片描述

很高兴,我们的猜想是正确的。所以Java线程的创建会调用系统中的线程创建函数。

todo2:暂未去理顺这个线程的创建和启动流程源码分析

思考:线程执行为什么不能直接调用run()方法,而要调用start()方法?

Thread#start()源码分析.jpg

这边不需要懂:

我大概记一下代码的位置有空回来复习:

1.D:\share\Ubuntu5+openjdk8\openjdk\jdk\src\share\native\java\lang\Thread.c

#include "jni.h"
#include "jvm.h"

#include "java_lang_Thread.h"

#define THD "Ljava/lang/Thread;"
#define OBJ "Ljava/lang/Object;"
#define STE "Ljava/lang/StackTraceElement;"
#define STR "Ljava/lang/String;"

#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

#undef THD
#undef OBJ
#undef STE
#undef STR

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

2.D:\share\Ubuntu5+openjdk8\openjdk\hotspot\src\share\vm\prims\jvm.cpp

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example:  we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    // Ensure that the C++ Thread and OSThread structures aren't freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }

3.D:\share\Ubuntu5+openjdk8\openjdk\hotspot\src\os\linux\vm\os_linux.cpp

pthread_create() 生成os的线程

总结:

java中线程和操作系统中线程是一一对应的,当调用线程的start方法,会调用线程中本地方法start0,然后会调用Thread.c中JVM_StartThread,然后会调用jvm.cpp中对应的方法,再然后会调用操作系统中pthread_create函数来创建线程,pthread_create创建好线程回调java类中run方法。

在这里插入图片描述

原文链接:https://blog.csdn.net/qq_36434742/article/details/106682656

推荐阅读