美文网首页并发
java线程模型

java线程模型

作者: loveFXX | 来源:发表于2019-11-19 22:32 被阅读0次

    Thread的start()方法

    示例代码
    package com.thread;
    public class ThreadTest {
    ![pthread_create.png](https://img.haomeiwen.com/i7310356/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方法也是类似的过程。

    相关文章

      网友评论

        本文标题:java线程模型

        本文链接:https://www.haomeiwen.com/subject/yilgictx.html