juc

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

2020-06-17  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读