模拟java线程

java中的线程与操作系统中的线程有什么区别

首先我们来查看 lunix 中的线程,通过lunix中提供的线程原语 man pthread_create 命令查看
查看lunix中的线程方法

执行上面的方法。我们可以看到
在这里插入图片描述

根据man配置的信息可以得出 pthread_create 会创建一个线程,这个函数是linux系统的函数,可以用C或者C++直接调用,上面信息也告诉程序员这个函数在pthread.h中,需要导入
这个函数 有四个参数

pthread_t *thread传出参数,调用之后会传出被创建线程的id定义 pthread_t pid; 继而取地址 &pid
const pthread_attr_t *attr传出参数,调用之后会传出被创 建线程的id线程属性,关于线程属性是linux 的知识在学习pthread_create函数的时 候一般穿NULL,保持默认属性
void *(*start_routine) (void *)线程的启动后的主体函数 相当于java当中的run需要你定义一个函数,然后传函 数名即可
void *arg主体函数的参数如果没有可以传NULL

我们通过C来调用lunix的 pthread_create 方法

#include <pthread.h>//头文件 ,C库
#include <stdio.h>

pthread_t pid;//定义一个变量,接受创建线程后的线程id 
 
 //定义线程的主体函数 
 void* thread_entity(void* arg) {
	while(1){
		usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		printf("i am new Thread!\n"); 
	}
  }
  
 //main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程 
 int main(){
 	//调用操作系统的函数创建线程,注意四个参数 ,调用的是上面的函数 
 	pthread_create(&pid,NULL,thread_entity,NULL);
	
	//防止创建完线程之后主线程关闭,这里用while循环来一直循环
	while(1){
		usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		printf("main\n");
	}
	return 0;
 } 

编译C文件 gcc -o thread thread.c -pthread
-o 表示编译之后的文件 ,如果不指定,就会默认的编译为.out文件
在这里插入图片描述
编译之后会产出一个 thread文件
在这里插入图片描述我们来执行这个 thread 文件 在该目录下执行 ./thread
在这里插入图片描述
可以看出我们调用了,操作系统的线程

那我们回到java中,那java中线程和操作系统中的线程有什么区别,我们可以通过查看java的线程的start方法源码
在这里插入图片描述
可以得出看到java调用start0方法,而start0()方法是native修饰的本地方法
因此我们猜测出,java的线程调用C文件,C文件调用操作系统函数
在这里插入图片描述
java 调用c ,c 调用 Hotspot , Hotspot调用 os函数。
这里有一个问题?
  为什么C不直接调用os函数,而是调用Hotspot的?

  因为我们java提供的有很多api,如果我们这些api对应到这c文件中,那我们又会有很多c文件,和本地方法。
所有当我们调用c的时候,其实在Hotspot中也创建了一个对象,javaThread,这个对象调用os函数, 抽象出来。

 
 
下面我们来验证run方法是pthread_create的第三个参数,思路如下:
  1 , 通过java启动一个线程(os线程) — 他的主体函数是C程序提供的函数 ( jni调用 )
  2 , 验证run ----- 通过java启动线程,回调我的run方法

验证:
提供一个java文件:

public class LubanThread {

   static {
       // 装载c文件到内存中,提供一个c 文件,编译成这个样子就行了
       // c文件提供start0 方法,当我们调用start0 的时候就回去找到对应的c文件中的方法,在c文件中调用 lunix的线程方法
       System.loadLibrary( "LubanThreadNative" );
   }
   public static void main(String[] args) {
       LubanThread lubanThread =new LubanThread();
       lubanThread.start0();
   }

   public void run(){
       System.out.println("I am new Thread!!");
   }

   private native void start0();
}

通过javac 编译此文件
在这里插入图片描述
在这里插入图片描述
编译完成之后,要生成 .h文件,在c文件中 引入改文件
上面的报错是因为的java的版本高于 1.7,所以就用不了

在这里插入图片描述

这个就是编译后的c 文件, 我们的start0 方法被编译成了 Java_LubanThread_start0
此处非常重要
在这里插入图片描述

这个时候我们来编写c文件

#include <pthread.h>//头文件 ,C库
#include <stdio.h>
#include "RuhrThread.h" // java生成的文件

pthread_t pid;//定义一个变量,接受创建线程后的线程id 
 
 //定义线程的主体函数 
 void* thread_entity(void* arg) {
	while(1){
		usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		printf("i am new Thread!\n"); 
	}
  }
  
 //main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程 
 //int main(){
 	//调用操作系统的函数创建线程,注意四个参数 ,调用的是上面的函数 
 	//pthread_create(&pid,NULL,thread_entity,NULL);
	
	//防止创建完线程之后主线程关闭,这里用while循环来一直循环
	//while(1){
		//usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		//printf("main\n");
	//}
	//return 0;
 //} 
 
 
//这个方法要参考.h文件的15行代码,这里的参数得注意,你写死就行,不用明白为什么 
JNIEXPORT void JNICALL Java_LubanThread_start0(JNIEnv *env, jobject c1){

	//调用操作系统的函数创建线程,注意四个参数 ,调用的是上面的函数 
 	pthread_create(&pid,NULL,thread_entity,NULL);
	
	//防止创建完线程之后主线程关闭,这里用while循环来一直循环
	while(1){
		usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		printf("main\n");
	}
}

int main() {
    return 0; 
}

解析类,把这个thread.c编译成为一个动态链接库,这样在java代码里会被laod到内存 libLubanThreadNative这个命名需要注意libxx,xx就等于你java那边写的字符串
在这里插入图片描述
运行下面的命令生成 .so文件
gcc -fPIC -I /usr/lib/jvm/java-openjdk/include -I /usr/lib/jvm/java-openjdk/include/linux/ -shared -o libLubanThreadNative.so thread.c -pthread
此处要注意,lunix安装的是openjdk! openjdk! 不是jdk
在这里插入图片描述做完这一系列事情之后需要把这个.so文件加入到path,这样java才能load到
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libLubanThreadNative.so} 所在的路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/thread

在这里插入图片描述运行java文件. 我们来见证奇迹 java RuhrThread

在这里插入图片描述
我们完成了java通过jni调用c 语言文件,下一步我们来验证c反向调用java
我们来编写c文件:

#include <pthread.h>//头文件 ,C库
#include <stdio.h>
#include "RuhrThread.h"

pthread_t pid;//定义一个变量,接受创建线程后的线程id 
 
 //定义线程的主体函数 
 void* thread_entity(void* arg) {
	while(1){
		usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		printf("i am new Thread!\n"); 
	}
  }
  
 //main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程 
 //int main(){
 	//调用操作系统的函数创建线程,注意四个参数 ,调用的是上面的函数 
 	//pthread_create(&pid,NULL,thread_entity,NULL);
	
	//防止创建完线程之后主线程关闭,这里用while循环来一直循环
	//while(1){
		//usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		//printf("main\n");
	//}
	//return 0;
 //} 
 
 
//这个方法要参考.h文件的15行代码,这里的参数得注意,你写死就行,不用明白为什么 
//JNIEXPORT void JNICALL Java_RuhrThread_start0(JNIEnv *env, jobject c1){

	//调用操作系统的函数创建线程,注意四个参数 ,调用的是上面的函数 
 //	pthread_create(&pid,NULL,thread_entity,NULL);
	
	//防止创建完线程之后主线程关闭,这里用while循环来一直循环
	//while(1){
		//usleep(100); //usleep是睡眠的意思,那么这里的睡眠是让主线程睡眠
		//printf("main\n");
	//}
//}

// 这次我们来调用java中的run方法 
JNIEXPORT void JNICALL Java_RuhrThread_start0(JNIEnv *env, jobject c1){
	// 在Java中我们如何调用方法的呢?
	// class -- new Object --method
	// 在c语言中我们通过虚拟机来获取到对象   JNIEnv *env 代表虚拟机
	
	jclass cls; //类
	jobject obj; 
	jmethodID cid; //构造方法
	jmethodID rid; 
	jint ret=0;	
	
	cls=(*env)->FindClass(env,"RuhrThread"); // 通过虚拟机找到类
	
	// 通过类new对象
	if(cls==NULL){
		printf("cindClass error!\n"); 
		return; 
	}
	// 调用构造方法
	cid=(*env)->GetMethodID(env,cls,"<init>","()V");
	if(cid ==NULL){
		printf("query constructor error!\n");
		return;
	}
	
	// 创建对象
	obj=(*env)-> NewObject(env,cls,cid); 
	if(obj==NULL){
		printf("NewObjcct crror!\n");
	}
	
	// 获取run方法
	rid=(*env)->GetMethodID(env,cls,"run","()V");
	
	//执行run方法
	ret=(*env)->CallIntMethod(env,obj,rid,NULL);
	
	printf("finsh call method!\n");
	
}

int main() {
    return 0; 
}
// gcc -o threadNew threadNew.c -I /usr/lib/jvm/java-1.8.0-openjdk/include -I /usr/lib/jvm/java-1.8.0-openjdk/include/linux -L /usr/lib/jvm/java-1.8.0- openjdk/jre/lib/amd64/server -ljvm -pthread

// gcc -o thread thread.c -I /usr/lib/jvm/java-openjdk/include -I /usr/lib/jvm/java-openjdk/include/linux -I /usr/lib/jvm/java-openjdk/jre/lib/amd64/server -ljvm -pthread



//gcc -fPIC -I /usr/lib/jvm/java-openjdk/include -I /usr/lib/jvm/java-openjdk/include/linux/ -shared -o libLubanThreadNative.so thread.c -pthread

//export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:

编写完后,需要把编写的c进行编译,并且加入到jdk中去,运行一下命令
gcc -o thread thread.c -I /usr/lib/jvm/java-openjdk/include -I /usr/lib/jvm/java-openjdk/include/linux -I /usr/lib/jvm/java-openjdk/jre/lib/amd64/server -ljvm -pthread

在编译一下我们的java文件,命令如下:
gcc -fPIC -I /usr/lib/jvm/java-openjdk/include -I /usr/lib/jvm/java-openjdk/include/linux/ -shared -o libLubanThreadNative.so thread.c -pthread

再将java文件加入到jdk中去
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/thread

最后执行: java RuhrThread
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
完成java调用C,C在调用java

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值