首先我们知道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 环境下.而具体放的路径可以随意,但是最好跟你的包路径相同,否则你执行是需要移动文件路径
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文件的根包路径上.
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文件,入图所示
5.添加环境变量
把so文件所在的目录添加到系统变量,不然java还是load不到
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/user/test
6.运行Java程序
最后我们运行Java文件即可以
Java xxx.xxx.MyThread
得到的结果如同所示: