首先我们知道Java中的线程类是Thread类,而我们知道启动线程采用的是start方法,而start方法中会调用到一个native的start0方法,而Java中的native方法是通过jni的方式去调用底层C(C++)程序.本篇博客模拟Java中的创建线程的整个过程.
首先我们要知道操作系统中创建线程的方法,在lInux中采用方法是pthread_create()方法

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

其中的四个参数分别表示的含义为:

参数名称

参数定义

参数解释

pthread_t *thread

传出的参数,调用后返回的线程ID

定义一个线程ID

const pthread_attr_t *attr

线程属性,关于线程属性是linux的知识

一般传NULL,保持默认属性

void (start_routine)(void *)

线程的启动后的主体函数

需要你定义一个函数,然后传函数名即可

void *arg

主体函数的参数

没有可以传null

接着我们继续看一下Java中的线程大概的执行方法

public void test(){
        Thread thread=new Thread(){
            @Override
            public void run() {
                System.out.println("this is thread!");
            }
        };
        thread.start();
    }

根据代码可以分析得,一个线程启动需要调用到线程的start方法,而通过start方法会继续调用到一个native的start0方法,接着通过JNI调用到操作系统的pthread_create方法.
根据这个流程我们就可以模拟出自己的线程方法.
1.首先模拟一个Java调用native方法

public class MyThread {
    static{
        System.loadLibrary("TestMyThread");
    }
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        myThread.start0();
    }
    private native void start0();
    
}

这里要说一下我们需要自己装载一下类库.然后将这个编写好的文件放入Linux 环境下.而具体放的路径可以随意,但是最好跟你的包路径相同,否则你执行是需要移动文件路径

java 多线程调用 短时间内 只执行一次 java多线程调用jni_java


2.我们就需要编译相关代码了,使用Javac 命令

javac xxx.java

此时会多出一个.class文件.
接着我们需要获取JNI文件,通过javac -h . 命令执行

Javac -h . xxx.java

此时文件中会多出一个.h文件,这个文件很重要,我们需要根据这个文件进行c程序的编写.打开这个文件我们可以看到一下内容:

#include <jni.h>
/* Header for class thread_MyThread */

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

#ifdef __cplusplus
}
#endif
#endif

我们需要将方法名复制一下,我们编写相关C语言代码需要用到.
3.编写C语言代码
接着我们就可以编写我们的C语言代码了,具体代码如下

#include <pthread.h>
#include <stdio.h>
#include <jni.h>
//定义变量接收线程id
pthread_t pid;
//线程的主主体方法, 相当于Java中的run
void * thread_entity(){
	while(1){
	   //睡眠100毫秒
	   usleep(100);
	  //打印
	  printf("I am new Thread \n ");
	}	
}

Java_thread_MyThread_start0(JNIEnv *evn, jobject c1){
	pthread_create(&pid,NULL,thread_entity,NULL);
	while(1){
	//睡眠100毫秒
	usleep(100);
	//打印
	printf(" I an main \n");
	}
}

这里有一个需要引入jni的库否则编译C语言编程SO文件时会报错.编写完后,我们继续将C文件上传至Linux环境上,你也可以在Linux上直接编写.文件最好上传到我们之前上传java文件的根包路径上.

java 多线程调用 短时间内 只执行一次 java多线程调用jni_java_02


4.将C程序编译程SO程序

编译这个myThreadC.c为一个so文件,这样才能被java程序加载到,具体命令为

gcc -fPIC -I /usr/lib/jvm/java-1.8.0-openjdk/include -I /usr/lib/jvm/java-1.8.0-openjdk/include/linux -shared -o libxxxll.so myThreadC.c

注:libxxx.so中的xxx是在Java代码中装载的库

static{
        System.loadLibrary("TestMyThread");
    }

此时文件下会多出一个.so文件,入图所示

java 多线程调用 短时间内 只执行一次 java多线程调用jni_java_03


5.添加环境变量

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

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/user/test

6.运行Java程序
最后我们运行Java文件即可以

Java xxx.xxx.MyThread

得到的结果如同所示:

java 多线程调用 短时间内 只执行一次 java多线程调用jni_#include_04