java线程模型

Thread的start()方法

示例代码
package com.thread;
public class ThreadTest {
![pthread_create.png](https://upload-images.jianshu.io/upload_images/7310356-b1e7bf980513efef.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    public static void main(String[] args) {
        Thread thread = new Thread( () -> System.out.println("thread run......") );
        thread.start();
    }
}
调用过程

start()方法调用


start.png

start0()本地方法


start0.png

调用本地方法如何调用到操作系统
通过native本地方法会首先经过C语言代码进行方法映射再调用hostpot虚拟机生成一个虚拟机类最后调用操作系统函数,最终会创建一个线程pthread_create

为什么不直接调用操作系统
java虚拟机(C/C++)创建一个对象javaThread,与java代码对象一一对应。多个中间层可控
JDK包中提供的java库、(C文件)调用操作系统函数、(C++)调用虚拟机的代码。hostpod虚拟机(win版本通过java.exe运行)
(java start thread对应操作系统通过 pthread_creat创建。java sync 就是os thread锁的原理)
thread.c


threadc.png

jvm.cpp
jvmcpp.png

os_linux.cpp
pthread_create.png

对于Thread线程类 start()方法是要调用操作系统方法pthread_create,我们不能写自己的逻辑。所以会由操作系统回调到run方法通过pthread_create创建线程传一个java_start方法(run方法是pthread_create第三个参数)
java_start.png

JNI调用过程测试

示例代码
public class MyThread {
    static {
        System.loadLibrary( "MyThreadNative" );
    }
    public static void main(String[] args) {
        MyThread thread = new MyThread( );
        thread.start0();
    }
    public void run(){
        System.out.println("jni MyThread run method");
    }
    private native void start0();
}
编译成class文件

MyThread.java编译成MyThread.class文件(javac MyThread.java)


javac.png
生成.h头文件

MyThread.class生成MyThread.h文件(javah MyThread)


javah.png

Mythread.h


Mythreadh.png
编写C文件

thread.c 需要include引用生成的MyThread.h文件,在MyThread.h文件中,生成的Java_MyThread_start0方法需要在这里使用、参数类型、返回结果也需要相同。这段代码测试的意义在于调用操作系统(Linux)函数pthread_create(&pid,NULL,thread_entity,NULL);函数会调用到第三个参数的thread_entity()方法的调用。测试结果出现I am thread_entity和i am main打印,说明完成了MyThread 类的start()方法的JNI调用

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

pthread_t pid;

void* thread_entity(void* arg)
{
while(1){
  printf("I am thread_entity\n");
  }
}

JNIEXPORT void JNICALL Java_MyThread_start0(JNIEnv *env, jobject c1)
{
  pthread_create(&pid,NULL,thread_entity,NULL);
  while(1){
    printf("i am main \n");
  }
}

int main(){
  return 0;
}
生成so文件

thread.c文件生成so文件(lib***.so形式,中间是System.loadLibrary( "MyThreadNative" );所引用的)
gcc thread.c -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" -fPIC -shared -o libMyThreadNative.so

.so文件加入到path

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{libMyThreadNative.so}所在的路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/thread

运行

运行MyThread类(java MyThread)


javaMyThread.png

回调run()方法测试

JNI反射回调MyThread类的run()方法
由于上面示例代码MyThread.class类中已经包含run()方法,所以生成的MyThread.h文件可以使用。只需要重新编写thread.c文件调用run()方法就可以了

thread.c

#include <pthread.h>
#include <stdio.h>
#include "MyThread.h"
JNIEXPORT void JNICALL Java_MyThread_start0(JNIEnv *env, jobject cl){
    jclass cls;
    jobject obj;
    jmethodID cid;
    jmethodID rid;
    jint ret = 0;
    cls = (*env)->FindClass(env, "MyThread");
    if(cls == NULL){
        printf("FindClass null");
        return;
    }
    cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    if(cid == NULL){
       printf("constructor null");
       return;
    }   
    obj = (*env)->NewObject(env, cls, cid);
    if(obj == NULL){
       printf("NewObject null");
       return;
    }
    rid = (*env)->GetMethodID(env, cls, "run", "()V");  
    ret = (*env)->CallIntMethod(env, obj, rid, NULL);   
    printf("finsh call method\n");       
} 
int main(){
    return 0; 
}

参数解释:
1、JNIEnv:相当于虚拟机环境
2、jclass : c得到的java类
3、jobject :c得到的java对象
4、cid :c得到的java类的构造方法
5、rid :c得到的java类的run方法
FindClass()、GetMethodID()、CallIntMethod()都是C语言的方法

thread.c文件生成so文件

首先有JAVA_HOME环境,可以使用全路径
gcc thread.c -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" -fPIC -shared -o libMyThreadNative.so

导入路径

export LD_LIBRARY_PATH=LD_LIBRARY_PATH:so文件所在的路径 如:/root/thread/libMyThreadNative.so export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/root/thread/

执行java程序

java Mythread


jnirun.png

pthread_create线程控制原语

man pthread_create (linux通过man获取)

NAME
       pthread_create - create a new thread

SYNOPSIS
       #include <pthread.h>

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

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_create()  function starts a new thread in the calling process.  The new thread starts execution by invoking start_routine(); arg is passed as the sole argu‐
       ment of start_routine().

       The new thread terminates in one of the following ways:

       * It calls pthread_exit(3), specifying an exit status value that is available to another thread in the same process that calls pthread_join(3).

       * It returns from start_routine().  This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.

       * It is canceled (see pthread_cancel(3)).

       * Any of the threads in the process calls exit(3), or the main thread performs a return from main().  This causes the termination of all threads in the process.

       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; this structure is ini‐
       tialized using pthread_attr_init(3) and related functions.  If attr is NULL, then the thread is created with default attributes.

       Before  returning,  a  successful  call  to  pthread_create() stores the ID of the new thread in the buffer pointed to by thread; this identifier is used to refer to the
       thread in subsequent calls to other pthreads functions.

       The new thread inherits a copy of the creating thread's signal mask (pthread_sigmask(3)).  The set of pending signals for the new thread is empty  (sigpending(2)).   The
       new thread does not inherit the creating thread's alternate signal stack (sigaltstack(2)).

       The new thread inherits the calling thread's floating-point environment (fenv(3)).

       The initial value of the new thread's CPU-time clock is 0 (see pthread_getcpuclockid(3)).

   Linux-specific details
       The new thread inherits copies of the calling thread's capability sets (see capabilities(7)) and CPU affinity mask (see sched_setaffinity(2)).

RETURN VALUE
       On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.

ERRORS
       EAGAIN Insufficient resources to create another thread.

       EAGAIN A  system-imposed  limit on the number of threads was encountered.  There are a number of limits that may trigger this error: the RLIMIT_NPROC soft resource limit
              (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached; the kernel's system-wide limit on  the  number  of  pro‐
              cesses and threads, /proc/sys/kernel/threads-max, was reached (see proc(5)); or the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)).

       EINVAL Invalid settings in attr.

       EPERM  No permission to set the scheduling policy and parameters specified in attr.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────┬───────────────┬─────────┐
       │Interface        │ Attribute     │ Value   │
       ├─────────────────┼───────────────┼─────────┤
       │pthread_create() │ Thread safety │ MT-Safe │
       └─────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       See  pthread_self(3)  for further information on the thread ID returned in *thread by pthread_create().  Unless real-time scheduling policies are being employed, after a
       call to pthread_create(), it is indeterminate which thread—the caller or the new thread—will next execute.

       A thread may either be joinable or detached.  If a thread is joinable, then another thread can call pthread_join(3) to wait for the thread to  terminate  and  fetch  its
       exit  status.   Only when a terminated joinable thread has been joined are the last of its resources released back to the system.  When a detached thread terminates, its
       resources are automatically released back to the system: it is not possible to join with the thread in order to obtain its exit status.  Making a thread detached is use‐
       ful for some types of daemon threads whose exit status the application does not need to care about.  By default, a new thread is created in a joinable state, unless attr
       was set to create the thread in a detached state (using pthread_attr_setdetachstate(3)).

       On Linux/x86-32, the default stack size for a new thread is 2 megabytes.  Under the NPTL threading implementation, if the RLIMIT_STACK soft resource limit  at  the  time
       the  program  started has any value other than "unlimited", then it determines the default stack size of new threads.  Using pthread_attr_setstacksize(3), the stack size
       attribute can be explicitly set in the attr argument used to create a thread, in order to obtain a stack size other than the default.

BUGS
       In the obsolete LinuxThreads implementation, each of the threads in a process has a different process ID.  This is in violation of the POSIX threads  specification,  and
       is the source of many other nonconformances to the standard; see pthreads(7).

函数参数意义:
pthread_t *thread 传出参数,调用之后会传出被创建的线程id 定义 pthread_t pid; 取地址 &pid
const pthread_attr_t *attr 线程属性,在测试pthread_create函数的时传NULL,保持默认属性
void (start_routine) (void *) 线程启动后的主体函数,相当于java中的run
void *arg 主体函数参数,没有可以传NULL

总结:

java对象调用native本地方法会首先经过C语言代码进行方法映射再调用hostpot虚拟机生成一个虚拟机类最后调用操作系统函数,例如:Thread类的start()方法会调用操作系统函数pthread_create创建一个线程(不是跟模拟的一样通过JNI调用,是通过C++技术调用的thread->run();)。其他native方法也是类似的过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,679评论 6 526
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,610评论 3 411
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,336评论 0 372
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,146评论 1 306
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,001评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,522评论 1 318
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,633评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,777评论 0 283
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,291评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,272评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,410评论 1 363
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,977评论 5 354
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,671评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,086评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,299评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,991评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,397评论 2 369