java线程底层源码学习

用了这么久的线程,都不知道底层到底怎么实现的,觉得有必要花时间研究一下。

c语言线程例子

这是一段c代码,mutexLock使两个线程交替执行。

#include<stdio.h>
#include<pthread.h>

void* run(void* args){
	while(1){
		usleep(1000);
		printf("sub\n");
	}
}
int main(){
	pthread_t tid;
	pthread_create(&tid,NULL,run,NULL);
	while(1){
		usleep(1000);
		printf("main\n");
	}
	return 0;
}

gcc -o thread.out pthread.c -lpthread
在centos上用上面的指令编译生成thread.out文件
gcc的安装请看gcc安装
-o 是输出,不写的话会默认命名为a.out
thread.out是输出的文件名
pthread.c是上面的c代码
-lpthread加上这个 解决undefined reference to pthread_create bug
./thread.out 执行这个c代码
结果正是两个线程交替执行。

操作系统层面的线程创建

使用命令man pthread_create查看pthread_create函数相关注释

       #include <pthread.h>
       int pthread_create(pthread_t *thread,//新创建的线程ID指向的内存单元
        				const pthread_attr_t *attr, //pthread_attr_t 结构体,attr用于确定线程属性
        				void *(*start_routine) (void *), //start_routine函数指针
      					void *arg);默认为NULL。若上面函数需要参数,将参数放入结构中并将地址作为arg传入
 		
       Compile and link with -pthread.	//编译和链接时需要加 -pthread
 
		DESCRIPTION
		//pthread_create函数的功能是在调用进程中启动一个新线程
        The pthread_create() function starts a new thread in the calling process.
        //新线程通过调用 start_routine() 开始执行;
        The new thread starts execution by invoking start_routine(); 
        //arg 作为 start_routine() 的唯一参数传递。
        arg is passed as the sole argument of start_routine().
 		
 		
		//这个线程以下列几种方式终止
       The new thread terminates in one of the following ways:
       //调用 pthread_exit(3) 函数来指定一个退出状态值,
       1: It calls pthread_exit(3), specifying an exit status value 
         //可以在同一进程的其他线程中调用  pthread_join(3) 函数来获得这个状态值。
         that is available to another thread in the same process that calls pthread_join(3).
        // 从 start_routine() 函数中返回:
       2: It returns from start_routine().
       //这相当于调用 pthread_exit(3),并且返回值由 return 声明提供。
        This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.
        //它被取消(参见 pthread_cancel(3))。
       3: It is canceled (see pthread_cancel(3)).
       //进程中的任何线程调用了 exit(3)
       4: Any of the threads in the process calls exit(3), 
       //或者主线程的 main() 函数中执行了 return
       or the main thread performs a return from main(). 
       //这会导致进程中所有线程都终止
       This causes the termination of all threads in the process.
  		
  		
  		//attr是第二个参数
  		//attr参数是pthread_attr_t结构体
       The attr argument points to a pthread_attr_t structure
      	//其内容在创建线程时用于确定新线程的属性
        whose contents are used at thread creation time to determine attributes for the new thread;
        // 使用 pthread_attr_init(3) 和相关函数初始化此结构
        this structure is initialized using pthread_attr_init(3) and related functions.
		//如果 attr 为 NULL,则使用默认属性创建线程
	    If attr is NULL, then the thread is created with default attributes.


 		//在返回之前,成功调用 pthread_create() 会
       Before returning, a successful call to pthread_create()
       //将新线程的 ID 存储在线程指向的缓冲区中
       stores the ID of the new thread in the buffer pointed to by thread;
       //这个ID用于在后续调用其他线程函数时引用该线程
        this identifier is used to refer to the thread in subsequent calls to other pthreads functions.
	
 
		RETURN VALUE
		//成功时,pthread_create() 返回 0
      	On success, pthread_create() returns 0;
      	// 如果出错,则返回错误编号,并且 thread 为野指针
        on error, it returns an error number, and the contents of *thread are undefined.
        

java调用c

public class JniThread {
    static{
        System.loadLibrary("JniThread");
    }
    public static void main(String[] args) {
        JniThread t = new JniThread();
        t.start();
    }
    private native void start();
}

把这个JniThread.java文件(去掉package com.lry.basic.juc)用xshell和xftp上传到centos /usr/local/c 目录下
然后javac JniThread.java生成JniThread.class文件
再javah JniThread生成JniThread.h文件,这个很重要,因为c的代码要根据这个文件来写
JniThread.h 如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JniThread */

#ifndef _Included_JniThread
#define _Included_JniThread
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniThread
 * Method:    start
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JniThread_start
  (JNIEnv *, jobject);//这里很重要

#ifdef __cplusplus
}
#endif
#endif

根据JniThread.h编写c代码 如下

#include<stdio.h>
#include<pthread.h>
#include "JniThread.h"//记得引入
void* run(void* args){
	while(1){
		usleep(1000);
		printf("sub\n");
	}
}
//从.h文件复制过来
JNIEXPORT void JNICALL Java_JniThread_start(JNIEnv *env, jobject c1){
	pthread_t tid;
	pthread_create(&tid,NULL,run,NULL);
	while(1){
		usleep(1000);
		printf("main\n");
	}
}

c编写好了,用xftp传到 /usr/local/c 下,我的.java,.class , .h和c文件全在这个路径下。
然后把c语言编译成so库文件,命令如下
gcc -fPIC -I/usr/local/java/jdk1.8.0_152/include/ -I/usr/local/java/jdk1.8.0_152/include/linux -shared -o libJniThread.so start.c
稍微说一下这个命令
-fPIC 是位置无关码,这个必须要
usr/local/java/jdk1.8.0_152这个是我的jdk路径
-I/usr/local/java/jdk1.8.0_152/include/为了引入jni.h
-I/usr/local/java/jdk1.8.0_152/include/linux 为了引入jni_md.h
-o 是输出
start.c自然是要编译的c文件
libJniThread.so这个命名必须libXX,XX是java代码的System.loadLibrary(“JniThread”);里面的字符串

so库生成好了,我们要把这个文件加入到java文件的path,这样java才能load到该文件
命令:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/c/
然后 java JniThread
结果是两个线程交替执行(crtl+c 退出执行)

c回调java

public class JniThread {
    static{
        System.loadLibrary("JniThread");
    }
    public static void main(String[] args) {
        JniThread t = new JniThread();
        t.start();
    }
    //这个方法被c回调
    public void run(){
        System.out.println("im java thread");
    }
    //调用c
    private native void start();
}

c代码

#include<stdio.h>
#include<pthread.h>
#include "JniThread.h"

JNIEXPORT void JNICALL Java_JniThread_start(JNIEnv *env, jobject c1){
	jclass cls;
	jobject obj;
	jmethodID cid;
	jmethodID rid;
	jint ret = 0;
	cls = (*env)->FindClass(env,"JniThread");
	if(cls==NULL){
		printf("find class error");
		return;
	}
	//构造器 
	cid = (*env)->GetMethodID(env,cls,"<init>","()V");
	if(cid==NULL){
		printf("find constructor error");
		return;
	}
	//new 对象 
	obj = (*env)->NewObject(env,cls,cid);
	if(obj==NULL){
		printf("new obj error");
		return;
	}
	//run方法 
	rid = (*env)->GetMethodID(env,cls,"run","()V");
	ret = (*env)->CallIntMethod(env,obj,rid,NULL);
	printf("finish");
}
int main(){
	return 0;
}

执行
1:gcc -o JniThread start.c -I /usr/local/java/jdk1.8.0_152/include -I/usr/local/java/jdk1.8.0_152/include/linux -L /usr/local/java/jdk1.8.0_152/jre/lib/amd64/server -ljvm -pthread(这个命令似乎不需要)
2:gcc -fPIC -I/usr/local/java/jdk1.8.0_152/include/ -I/usr/local/java/jdk1.8.0_152/include/linux -shared -o libJniThread.so start.c
3: javac JniThread.java
4:java JniThread
结果打印im java thread

线程调用链(个人理解)

java.start ->start0(native)->pthread_create(&tid,NULL,java_thread,NULL)

void java_thread(){
JNI反向调用java的run方法
}