Java线程与操作系统线程关系

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

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

注:
1. void *void指针可以指向任意类型的数据,就是说可以用任意类型的指针对void指针赋值。
2. 函数指针:将函数作为参数传入另一函数中。函数指针形式为:ElemType (*FunctionName)(ElemType, ElemType, ...)

在linux系统中,使用man pthread_create查看:

pthread_create

发现该函数在pthread.h文件中,作用是创建一个线程。

各个参数的含义是:

参数名字参数定义参数解释
pthread_t *thread传出参数,调用之后会传出被创建线程的id定义 pthread_t pid; 继而取地址&pid
const pthread_attr_t *attr线程属性,线程属性是linux的知识一般传NULL,保持默认属性
void (start_routine)(void *)线程启动后的主体函数需要定义一个函数,然后传函数名即可
void *arg主体函数的参数没有可以传null

在linux上启动一个线程:

//头文件
#include <pthread.h>
#include <stdio.h>
//定义一个变量,接受创建线程后的线程id
pthread_t pid;
//定义线程的主体函数
void* thread_entity(void* arg) {
    printf("i am a new Thread!\n");
}

int main() {
  // 调用操作系统的函数创建线程
  pthread_create(&pid, NULL, thread_entity, NULL);
  usleep(100);
  printf("main\n");
  return 0;
}

编译:gcc thread.c -o thread.out -pthread

2. Java中的线程

首先给出结论:Java中的线程和操作系统中的线程是一一对应的,Java用start()启动线程,start()调用start0()本地方法(通过JNI调用),继而调用Linux中pthread_create()创建线程,Java线程中的run()作为参数传入pthread_create()。

首先创建一个Java线程:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WdOV3Yq7-1603992787222)(https://gitee.com/xMustang/notespic/raw/master/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/start()]%E8%B0%83%E7%94%A8start0().png)

在HotSpot VM中,start0()对应JVM_StartThread方法,在JVM_StartThread方法中会实例化一个C++对象JavaThread,如果是Linux系统,会调用pthread_create()方法创建线程。

3. Java利用JNI调用本地方法

  1. 编写Java类,并编译(这里编译好这个java类主要为了后面运行它来测试)

    package com.shadow.app;
    public class EnjoyThread {
        //装载库,保证JVM在启动时就会装载,故而一般是static
        static {
            System.loadLibrary("EnjoyThreadNative");
        }
    
        public static void main(String[] args) {
            EnjoyThread enjoyThread = new EnjoyThread();
            enjoyThread.start0();
        }
    
        private native void start0();
    }
    

    编译成class文件,最好是在包的目录下面运行javac命令,javac EnjoyThread.java

  2. 编译一个头文件
    最好是在包路径下面运行,javac -h . EnjoyThred.java

    编译好的结果是多了一个.h的文件。

    编译出.h文件

  3. 修改threadNew.c文件

    打开刚刚编译好的.h文件,复制方法名。

    复制.h文件方法名

    threadNew.c:

    #include <pthread.h>
    #include <stdio.h>
    #include "com_shadow_app_EnjoyThread.h"
    
    //定义变量接受线程id
    pthread_t pid;
    
    //线程的主体方法相当于 java当中的run
    void *thread_entity(void *arg) {
        //子线程死循环
        while (1) {
            //睡眠100毫秒
            usleep(100);
            //打印
            printf("I am new Thread\n");
        }
    }
    
    //这个方法名字需要参考.h当中的方法名字,打开.h文件,复制方法名过来
    //参数固定
    Java_com_shadow_app_EnjoyThread_start0(JNIEnv *env, jobject c1) {
        //调用linux的系统的函数创建一个线程
        pthread_create( &pid, NULL, thread_entity, NULL);
        //主线程死循环
        while (1) {
            //睡眠100毫秒
            usleep(100);
            //打印
            printf("I am main\n");
        }
    }
    
  4. 编译threadNew.c为一个so文件,这样才能被java程序加载到。

    gcc -fPIC -I /home/shadows/soft/jdk11/include -I /home/shadows/soft/jdk11/include/linux -shared -o libxxx.so threadNew.c
    

    注意这个libxxx; lib是固定的xxx可以随意,但是得和java代码对应。

    static {
        //如果你是libxxx;这里就写xxx
        System.loadLibrary("xxx");
    }
    

    下面是编译好的目录结构:

    调用JNI时编译好的目录结构

  5. 把so文件所在的目录添加到系统变量,不然java还是load不到

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/shadows/tools/enjoy/com/shadow/app/
    
  6. 最后运行前面写好的那个java文件(到包的外面去运行java命令),就可以使用JNI调用。

    java com.xxxx.xxxx.EnjoyThread
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值