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.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调用本地方法
-
编写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
-
编译一个头文件
最好是在包路径下面运行,javac -h . EnjoyThred.java
编译好的结果是多了一个.h的文件。
-
修改threadNew.c文件
打开刚刚编译好的.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"); } }
-
编译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"); }
下面是编译好的目录结构:
-
把so文件所在的目录添加到系统变量,不然java还是load不到
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/shadows/tools/enjoy/com/shadow/app/
-
最后运行前面写好的那个java文件(到包的外面去运行java命令),就可以使用JNI调用。
java com.xxxx.xxxx.EnjoyThread