本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
从 Hotspot 虚拟机角度来分析 Java 线程启动
zhuxiaoqiang · 210浏览 · 发布于2021-09-10 +关注

Java 线程其实是映射到操作系统的内核线程上的,所以 Java 线程基本上也就是操作系统在进行管理。在 Linux系统中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的多个线程之间是共享资源的。

基本概念

Java 线程其实是映射到操作系统的内核线程上的,所以 Java 线程基本上也就是操作系统在进行管理。在 Linux系统中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的多个线程之间是共享资源的。

简单说明:本文基于 openjdk 1.8 进行

线程状态

每种线程状态的切换条件, 以及调用方法如下图所示 :

线程具有以下几种状态 Java 的线程状态在 Thread.State 枚举中定义代码如下

public enum State { 
    //新创建,未启动 
    NEW, 

    //在jvm 中运行,也可能正在等待操作系统的其他资源 
    RUNNABLE, 

    //阻塞,并且正在等待监视器锁 
    BLOCKED, 

    //处于等待状态的线程,正在等待另一个线程执行特定的操作 
    WAITING, 

    //限期等待, 可以设置最大等待时间 
    TIMED_WAITING, 

    //结束 
    TERMINATED; 
}

    线程创建

    继承 Thread 类, 代码如下:

    class PrimeThread extends Thread { 
        long minPrime; 
        PrimeThread(long minPrime) { 
            this.minPrime = minPrime; 
        } 
    
        public void run() { 
            // compute primes larger than minPrime 
            . . . 
            } 
    } 
    
    // 启动线程 
    PrimeThread p = new PrimeThread(143); 
    p.start();

      实现 Runable 接口, 代码如下 (通常推荐使用这种方式):

      class PrimeRun implements Runnable { 
          long minPrime; 
          PrimeRun(long minPrime) { 
              this.minPrime = minPrime; 
          } 
      
          public void run() { 
              // compute primes larger than minPrime 
              . . . 
              } 
      } 
      
      // 启动线程 
      PrimeRun p = new PrimeRun(143); 
      new Thread(p).start();

        hotspot 源码

        JNI 机制

        JNI 是 Java Native Interface 的缩写,它提供了若干的 API 实现了Java和其他语言的通信(主要是C和C++)。

        JNI的适用场景 当我们有一些旧的库,已经使用C语言编写好了,如果要移植到Java上来,非常浪费时间,而JNI可以支持Java程序与C语言编写的库进行交互,这样就不必要进行移植了。或者是与硬件、操作系统进行交互、提高程序的性能等,都可以使用JNI。需要注意的一点是需要保证本地代码能工作在任何Java虚拟机环境。

        • JNI的副作用 一旦使用JNI,Java程序将丢失了Java平台的两个优点:

        • 程序不再跨平台,要想跨平台,必须在不同的系统环境下程序编译配置本地语言部分。

        程序不再是绝对安全的,本地代码的使用不当可能会导致整个程序崩溃。一个通用规则是,调用本地方法应该集中在少数的几个类当中,这样就降低了Java和其他语言之间的耦合。

        举个例子 这块操作比较多,可以参考如下的资料

        https://www.runoob.com/w3cnote/jni-getting-started-tutorials.html

        启动流程

        启动流程如下

        线程启动

        Java 创建线程 Thread 实例之后,是通过 start 方法进行启动该线程,通知执行。在 start 方法的内部,调用的是 start0() 这个本地方法。我们可以从该方法为入口分析 JVM 对于 Thread 的底层实现。

        public synchronized void start() { 
            // 判断线程状态 
            if (threadStatus != 0) 
                throw new IllegalThreadStateException(); 
        
            // 添加到组 
            group.add(this); 
        
            boolean started = false; 
            try { 
                // 启动线程 
                start0(); 
                started = true; 
            } finally { 
                try { 
                    if (!started) { 
                        group.threadStartFailed(this); 
                    } 
                } catch (Throwable ignore) { 
                    /* do nothing. If start0 threw a Throwable then 
                      it will be passed up the call stack */ 
                } 
            } 
        } 
        
        private native void start0();

          start0() 是一个本地方法,咱们按照 JNI 规范可以到 hotspot 虚拟源码中查找 java_lang_Thread_start0 这个函数。定义如下:

          /* 
           * Class:     java_lang_Thread 
           * Method:    start0 
           * Signature: ()V 
           */ 
          JNIEXPORT void JNICALL Java_java_lang_Thread_start0 
            (JNIEnv *, jobject);

            通过注释 Method: start0 我可以猜到,在 jvm 的内部也可能会存在 start0 这个方法,于是我又搜索了一下这个方法,找到了 Thread.c 文件。可以看到里面有一个 Java_java_lang_Thread_registerNatives() 方法,这就是用来初始化在 Thread.java 与其他方法的绑定,并且在 Threa.java 的第一个 static 块中就调用了这个方法,保证这个方法在类加载中是第一个被调用的方法。这个 native 方法的作用是为其他 native 方法注册到JVM中。代码如下所示:

            static JNINativeMethod methods[] = { 
                {"start0",           "()V",        (void *)&JVM_StartThread}, 
                {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread}, 
                {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive}, 
                {"suspend0",         "()V",        (void *)&JVM_SuspendThread}, 
                {"resume0",          "()V",        (void *)&JVM_ResumeThread}, 
                {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority}, 
                {"yield",            "()V",        (void *)&JVM_Yield}, 
                {"sleep",            "(J)V",       (void *)&JVM_Sleep}, 
                {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread}, 
                {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames}, 
                {"interrupt0",       "()V",        (void *)&JVM_Interrupt}, 
                {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted}, 
                {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock}, 
                {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads}, 
                {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, 
                {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName}, 
            }; 
            
            #undef THD 
            #undef OBJ 
            #undef STE 
            #undef STR 
            
            JNIEXPORT void JNICALL 
            Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) 
            { 
                (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); 
            }


              再回到我们的 start0 方法,此时我们就去查找 JVM_StartThread 方法是在他是在/hotspot/src/share/vm/prims/jvm.cpp 这个文件里面:

              JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) 
                JVMWrapper("JVM_StartThread"); 
                JavaThread *native_thread = NULL; 
              
                // We cannot hold the Threads_lock when we throw an exception, 
                // due to rank ordering issues. Example:  we might need to grab the 
                // Heap_lock while we construct the exception. 
                bool throw_illegal_thread_state = false; 
              
                // We must release the Threads_lock before we can post a jvmti event 
                // in Thread::start. 
                { 
                  // Ensure that the C++ Thread and OSThread structures aren't freed before 
                  // we operate. 
                  MutexLocker mu(Threads_lock); 
              
                  // 1. 判断 Java 线程是否启动,如果已经启动,抛出异常 
                  if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { 
                    throw_illegal_thread_state = true; 
                  } else { 
                    // 2. 如果没有创建,则会创建线程  
                    jlong size = 
                           java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));   
                    size_t sz = size > 0 ? (size_t) size : 0; 
                    // 虚拟机创建 JavaThread, 该类内部会创建操作系统线程,然后关联 Java 线程   
                    native_thread = new JavaThread(&thread_entry, sz); 
              
                    if (native_thread->osthread() != NULL) { 
                      // Note: the current thread is not being used within "prepare". 
                      native_thread->prepare(jthread); 
                    } 
                  } 
                } 
              
                if (throw_illegal_thread_state) { 
                  THROW(vmSymbols::java_lang_IllegalThreadStateException()); 
                } 
              
                assert(native_thread != NULL, "Starting null thread?"); 
              
                if (native_thread->osthread() == NULL) { 
                  // No one should hold a reference to the 'native_thread'. 
                  delete native_thread; 
                  if (JvmtiExport::should_post_resource_exhausted()) { 
                    JvmtiExport::post_resource_exhausted( 
                      JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, 
                      "unable to create new native thread"); 
                  } 
                  THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), 
                            "unable to create new native thread"); 
                } 
              
                // 设置线程状态为 Runnable 
                Thread::start(native_thread); 
              
              JVM_END

                JavaThread 类的构造方法我们一起来看看,他是通过 os::create_thread 函数来进行创建 Java 对应的内核线程

                JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : 
                  Thread() 
                { 
                  if (TraceThreadEvents) { 
                    tty->print_cr("creating thread %p", this); 
                  } 
                  initialize(); 
                  _jni_attach_state = _not_attaching_via_jni; 
                  set_entry_point(entry_point); 
                  os::ThreadType thr_type = os::java_thread; 
                  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : 
                                                                     os::java_thread; 
                  // 创建Java线程对应的内核线 
                  os::create_thread(this, thr_type, stack_sz); 
                  _safepoint_visible = false; 
                }

                  os:create_thread 其实主要就是一个用来支持跨平台创建线程的, 以 Linux 为例 (hotspot/src/os/linux/vm/os_linux.cpp):

                  bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { 
                    // ... 
                     
                    // 创建 OSThread 内核线程对象 
                    OSThread* osthread = new OSThread(NULL, NULL); 
                    // 绑定 
                    thread->set_osthread(osthread); 
                  
                    pthread_t tid; 
                    // pthread_create 为 linux api 用来创建线程。 
                    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); 
                    // ...   
                    return true; 
                  }

                    我们可以通过 ubantu 的控制台来查询接口信息

                    • man pthread_create 来进行查询文档

                    通过文档我们可以了解,当 pthread_create 函数执行创建完线程之后会调用第三个参数传递过去的回调函数

                    • int ret = pthread_create(&tid, &attr, (void* ()(void)) java_start, thread);

                    在这里就是 java_start 函数

                    // Thread start routine for all newly created threads 
                    static void *java_start(Thread *thread) { 
                    
                      // 主要是调用 Thread 的 run 方法 
                      thread->run(); 
                    
                      return 0; 
                    }

                      在 thread.cpp 中 JavaThread::run 方法最终调用了 thread_main_inner 方法:

                      // The first routine called by a new Java thread 
                      void JavaThread::run() { 
                        
                      
                        // We call another function to do the rest so we are sure that the stack addresses used 
                        // from there will be lower than the stack base just computed 
                        thread_main_inner(); 
                      
                        // Note, thread is no longer valid at this point! 
                      }

                        在 thread_main_inner 方法内,在调用咱们之前创建 JavaThread 对象的时候传递进来的 entry_point 方法:

                        void JavaThread::thread_main_inner() { 
                        
                          if (!this->has_pending_exception() && 
                              !java_lang_Thread::is_stillborn(this->threadObj())) { 
                            { 
                              ResourceMark rm(this); 
                              this->set_native_thread_name(this->get_thread_name()); 
                            } 
                            HandleMark hm(this); 
                            // 调用 entry_point 方法 
                            this->entry_point()(this, this); 
                          } 
                        
                          DTRACE_THREAD_PROBE(stop, this); 
                        
                          this->exit(false); 
                          delete this; 
                        }

                          通过上面的代码我们可以看到先创建了一个 JavaThread 对象, 然后传入了 thread_entry 方法

                          // JVM_StartThread 创建操作系统线程,执行  thread_entry 函数 
                          static void thread_entry(JavaThread* thread, TRAPS) { 
                            HandleMark hm(THREAD); 
                            Handle obj(THREAD, thread->threadObj()); 
                            JavaValue result(T_VOID); 
                            // Thrad.start() 调用 java.lang.Thread 类的 run 方法 
                            JavaCalls::call_virtual(&result, 
                                                    obj, 
                                                    KlassHandle(THREAD, SystemDictionary::Thread_klass()), 
                                                    vmSymbols::run_method_name(), 
                                                    vmSymbols::void_method_signature(), 
                                                    THREAD); 
                          }

                            我们再来看看我们 Java 中 Thread 类的 run 方法

                            public void run() { 
                                if (target != null) { 
                                    // Thread.run() 又调用 Runnable.run() 
                                    target.run();  
                                } 
                            }



                            相关推荐

                            PHP实现部分字符隐藏

                            沙雕mars · 1325浏览 · 2019-04-28 09:47:56
                            Java中ArrayList和LinkedList区别

                            kenrry1992 · 908浏览 · 2019-05-08 21:14:54
                            Tomcat 下载及安装配置

                            manongba · 970浏览 · 2019-05-13 21:03:56
                            JAVA变量介绍

                            manongba · 962浏览 · 2019-05-13 21:05:52
                            什么是SpringBoot

                            iamitnan · 1086浏览 · 2019-05-14 22:20:36
                            加载中

                            0评论

                            评论
                            我爱编程,我爱工作,更爱生活
                            小鸟云服务器
                            扫码进入手机网页