美文网首页juc
join() 源码分析之:怎么让线程按照顺序执行?

join() 源码分析之:怎么让线程按照顺序执行?

作者: 程序员阿奇 | 来源:发表于2020-06-17 11:30 被阅读0次

    问题:给定三个线程 A B C 怎么让 A-B-C 三个线程按照顺序执行完?
    问题分析:按照指定循序执行,也就是说线程A执行完再执行线程B,线程B执行完再执行线程C.
    如何实现?通过 join() 方法就可以实现:

    public static void main(String[] args) throws Exception {
            // 创建三个线程 A, B C
            Thread threadA = new Thread(() -> {
                System.out.println(Thread.currentThread() + "执行");
            }, "线程A");
    
            Thread threadB = new Thread(() -> {
                System.out.println(Thread.currentThread() + "执行");
            }, "线程B");
    
            Thread threadC = new Thread(() -> {
                System.out.println(Thread.currentThread() + "执行");
            }, "线程C");
    
            threadA.start();
            threadA.join();
            threadB.start();
            threadB.join();
            threadC.start();
            threadC.join();
        }
    

    运行结果如下:

    Thread[线程A,5,main]执行
    Thread[线程B,5,main]执行
    Thread[线程C,5,main]执行
    Process finished with exit code 0
    

    从运行结果来看,确实符合要求,那这是为什么呢,原理是什么呢?

    执行过程分析:
    main 线程启动开始从上到下执行,首先创建三个线程 A,B,C 然后开始执行 threadA.start(); 这时候 线程A调用native start0(),启动一个线程,然后执行 线程A run方法,即执行:System.out.println(Thread.currentThread() + "执行");,执行完后 main 线程开始执行threadA.join();
    源码如下:

        public final void join() throws InterruptedException {
            join(0);
        }
    
        public final synchronized void join(long millis)
        throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    

    由于millis ==0,所以会执行以下代码:

    if (millis == 0) {
          while (isAlive()) {
               wait(0);
           }
     }
    

    通过以上代码,你可能会发现这不是死循环吗,什么时候isAlive()false 呢? main线程开始执行该代码发现 isAlive() 方法 也是 native方法,赶紧打开JVM 源码查找,发现isAlive 定义(Thread.c):

    static JNINativeMethod methods[] = {
        {"start0",           "()V",        (void *)&JVM_StartThread},
        {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
        {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
         此处省略。。。
    };
    // JVM_IsThreadAlive 方法实现
    JVM_ENTRY(jboolean, JVM_IsThreadAlive(JNIEnv* env, jobject jthread))
      JVMWrapper("JVM_IsThreadAlive");
      oop thread_oop = JNIHandles::resolve_non_null(jthread);
      return java_lang_Thread::is_alive(thread_oop);
    JVM_END
    // is_alive 实现
    bool java_lang_Thread::is_alive(oop java_thread) {
      JavaThread* thr = java_lang_Thread::thread(java_thread);
      return (thr != NULL);
    }
    

    以上方法主要是判断当前线程是否存活,很显然当前线程mian线程处于运行状态,所以 isAlive()true,然后开始执行 wait(0)方法,main线程进入进入等待队列,等待被唤醒,那么什么时候被唤醒呢? 通过上一篇文章: 线程启动:源码的理解可以知道创建线程后会执行 线程的的 run 方法 线程 执行完后会调用线程的退出方法:

    void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
      assert(this == JavaThread::current(),  "thread consistency check");
    
      HandleMark hm(this);
      Handle uncaught_exception(this, this->pending_exception());
      this->clear_pending_exception();
      Handle threadObj(this, this->threadObj());
      assert(threadObj.not_null(), "Java thread object should be created");
    
      if (get_thread_profiler() != NULL) {
        get_thread_profiler()->disengage();
        ResourceMark rm;
        get_thread_profiler()->print(get_thread_name());
      }
    // 此处省略非关键代码
    // Notify waiters on thread object. 
    // 唤醒等待的线程
    ensure_join(this);
    
    static void ensure_join(JavaThread* thread) {
      // We do not need to grap the Threads_lock, since we are operating on ourself.
      Handle threadObj(thread, thread->threadObj());
      assert(threadObj.not_null(), "java thread object must exist");
      ObjectLocker lock(threadObj, thread);
      // Ignore pending exception (ThreadDeath), since we are exiting anyway
      thread->clear_pending_exception();
      // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
      java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
      // Clear the native thread instance - this makes isAlive return false and allows the join()
      // to complete once we've done the notify_all below
      //清理本地线程实例,设置 isAlive 为 false
      java_lang_Thread::set_thread(threadObj(), NULL);
      // 唤醒线程
      lock.notify_all(thread);
      // Ignore pending exception (ThreadDeath), since we are exiting anyway
      thread->clear_pending_exception();
    }
    

    分析线程退出代码,发现:isAlive=false ,这样跳出while(isAlive()){wait(0)},main线程被唤醒,然后接着继续往下执行. threadB.start(); threadB.join(); threadC.start(); threadC.join(); 重复以上执行过程,就得到了我们想要的分析结果。

    如果您觉得该文章对您有帮助,点个赞就好,也可以分享给你朋友,如果写的不好还请多包涵,咱们可以探讨交流: qq:786063250

    相关文章

      网友评论

        本文标题:join() 源码分析之:怎么让线程按照顺序执行?

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