Thread.join()方法原理分析

如果现在有这样一个场景:在主线程中创建两个线程T1和T2,通过start启动线程后。我们想T1结束了再执行T2,等T1和T2都执行结束,才继续执行主线程,怎么能做到呢?
其实很简单,通过join就可以实现:

public class JoinDemo {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        t1.start();
        t1.join();
        t2.start();
        t2.join();
        System.out.println(count);
        for (int i = 0; i < 10000; i++){
            count ++;
        }
        System.out.println(count);
    }
}

像上面这样,通过join就能实现让主线程阻塞,等待T1执行完了再执行T2,T2完成再执行T1.

那到底是如何实现的呢?其实可以猜想一下,如果想让主线程阻塞,等待某一个线程执行完成后再继续执行,可以通过让主线程Wait在某一个锁上,等到子线程执行完成后,在唤醒主线程即可。那join是如何实现的呢?看join的源码如下:

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;
            }
        }
    }

会发现,果然,Join确实是通过wait实现的,是以被等待的线程为锁对象,将主线程wait在以被等待线程为锁对象的对象锁上。

有wait,必定会有notify,那在什么时候notify呢?既然主线程是为了等待被等待线程执行结束再继续执行,那应该是在被等待线程退出的时候调用notify。所以,应该找找JVM线程退出时候的源码:
在Thread.cpp中找到exit方法,会发现,在exit方法中调用了ensure_join方法,从注释上可以看出,这个方法就是为了在线程退出之前,唤醒wait在当前线程的对象锁上的其他线程。
在这里插入图片描述
继续看ensure_join方法的实现:

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
   java_lang_Thread::set_thread(threadObj(), NULL);
   lock.notify_all(thread);
   // Ignore pending exception (ThreadDeath), since we are exiting anyway
   thread->clear_pending_exception();
 }

发现,最终确实是调用了notifyAll方法。

为了加深对join的理解,其实我们为了实现文章开头的那个场景,并不需要调用join方法,只需要将主线程wait在被等待的线程的线程锁对象上即可,同样能实现和join相同的效果,因为join就是这样做的,所以我们可以这样去写(当然实际中肯定是用封装好的join最简单明了):

public class JoinDemo {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        new JoinDemo().fun();
    }

    private void fun() throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        t1.start();
        synchronized(t1){
            t1.wait();
        }
//        t1.join();
        t2.start();
        synchronized (t2){
            t2.wait();
        }
//        t2.join();
        System.out.println(count);
        for (int i = 0; i < 10000; i++){
            count ++;
        }
        System.out.println(count);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章