• 0.并发编程-openjdk编译调试、java线程模型


    解决问题: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
    ");
        }
        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
    ");
        }
        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
    ");
        }
    }
    void* start(){
        pthread_create(&pid,NULL,thread_entity,NULL);
        while(1){
    		usleep(100);
    		printf("I am  main
    ");
    	}
        
    }
    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
    ");
        }
    }
    
    /*
     * 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
    ");
       }
    
    
      }
    
    
    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!
    ")
        return;
      }
       //找到LubanThread类中无参的构造函数
      cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
      if (cid == NULL) {
        printf("GetMethodID Error!
    ");
        return;
      }
      //实例化一个对象
      obj = (*env)->NewObject(env, cls, cid);
      if(obj == NULL){
        printf("NewObject Error!
    ")
        return;
      }
      rid = (*env)->GetMethodID(env, cls, "run", "()V");
      //ret = (*env)->CallIntMethod(env, obj, rid, Null);
      // printf("Finsh call method!
    ")
        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
    ");
       }
    
    }
    
    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!
    ");
        }
        //找到LubanThread类中无参的构造函数
        cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        if (cid == NULL) {
            printf("Query constructor error!
    ");
        }
        //实例化对象
        obj = (*env)->NewObject(env, cls, cid);
        if (obj == NULL) {
            printf("NewObject error!
    ");
        }
        //查找run方法
        rid = (*env)->GetMethodID(env, cls, "run", "()V");
        if (rid == NULL) {
            printf("Query runMethod error
    ");
        }
        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
    ");
    	}
    }
    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!
    ");
        }
        //找到LubanThread类中无参的构造函数
        cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        if (cid == NULL) {
            printf("Query constructor error!
    ");
        }
        //实例化对象
        obj = (*env)->NewObject(env, cls, cid);
        if (obj == NULL) {
            printf("NewObject error!
    ");
        }
        //查找run方法
        rid = (*env)->GetMethodID(env, cls, "run", "()V");
        if (rid == NULL) {
            printf("Query runMethod error
    ");
        }
        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
    ");
    	}
    }
    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!
    ");
        }
        //找到LubanThread类中无参的构造函数
        cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        if (cid == NULL) {
            printf("Query constructor error!
    ");
        }
        //实例化对象
        obj = (*env)->NewObject(env, cls, cid);
        if (obj == NULL) {
            printf("NewObject error!
    ");
        }
        //查找run方法
        rid = (*env)->GetMethodID(env, cls, "run", "()V");
        if (rid == NULL) {
            printf("Query runMethod error
    ");
        }
        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
    ");
        }
    }
    //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:shareUbuntu5+openjdk8openjdkjdksrcshare ativejavalangThread.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:shareUbuntu5+openjdk8openjdkhotspotsrcsharevmprimsjvm.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:shareUbuntu5+openjdk8openjdkhotspotsrcoslinuxvmos_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

  • 相关阅读:
    四校联考【20171001】
    C语言基础知识
    页表和TLB
    python
    Cache组织方式
    On the Spectre and Meltdown Processor Security Vulnerabilities
    latex-组织文本
    深入理解计算机系统
    深入理解计算机系统-计算机系统漫游
    逻辑地址到物理地址的转换
  • 原文地址:https://www.cnblogs.com/tangliMeiMei/p/15323266.html
Copyright © 2020-2023  润新知