龙空技术网

你真得懂Thread.join吗?

java小当家 176

前言:

现时看官们对“vbnetthreadjoin”大致比较注意,同学们都需要学习一些“vbnetthreadjoin”的相关知识。那么小编同时在网摘上搜集了一些对于“vbnetthreadjoin””的相关知识,希望看官们能喜欢,姐妹们快快来了解一下吧!

Thread类中的join方法,用于等待某个线程执行结束。

简单示例

以下简单的代码,会让主线程等待子线程执行结束再执行。如果去掉t.join(),可能主线程就直接退出了,子线程都来不及执行。

package com.qcy.testJoin;/** * @author qcy * @create 2020/09/10 17:17:03 */public class Main {    public static void main(String[] args) throws InterruptedException {        Thread t = new Thread(() -> {            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }        });        t.start();        t.join();    }}
join源码分析

t.join()源码如下:

    public final void join() throws InterruptedException {        join(0);    }

接着是调用join的单参数重载方法,传入等待时间0,表示一直等待下去

这一点也可以从重载方法的注释中看到

单参数的重载方法如下:以主线程调用t.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;            }        }    }

(1)由于这里出现了synchronized,因此主线程需要拿到子线程对象t的锁。

(2)当millis为0的时候,循环判断isAlive(),即判断线程是否存活,如果存活,调用子线程对象的wait方法,传入0也是表示一直等待下去

    /**     * Tests if this thread is alive. A thread is alive if it has     * been started and has not yet died.     *     * @return  <code>true</code> if this thread is alive;     *          <code>false</code> otherwise.     */    public final native boolean isAlive();

(3)当主线程调用子线程对象的wait方法后,主线程释放掉锁,并进入等待状态。

(4)当子线程运行结束,不再是存活状态,那么主线程需要被唤醒,且需要再次获取到锁,才能继续运行join剩余的方法,运行结束后,主线程从join方法中返回,继续运行main剩余的方法。

JVM底层代码分析

问题来了,主线程什么时候被唤醒?唤醒代码又是在哪里的呢?

这要看jvm的底层代码,在thread.cpp代码中,地址thread.cpp地址

在线程退出的代码中,调用了ensure_join()方法

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {  ....  // Notify waiters on thread object. This has to be done after exit() is called  // on the thread (if the thread is the last thread in a daemon ThreadGroup the  // group should have the destroyed bit set before waiters are notified).  ensure_join(this);  ....  // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread  Threads::remove(this);}

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

java_lang_Thread::set_thread(threadObj(), NULL)语句,由上面的注释,可以了解到该方法会清除本地线程实例,将会使得isAlive()返回false,接着在调用完lock.notify_all(thread)语句后,即通知在此线程对象上的等待者,我们例子中的主线程在重新获得CPU分配的时间片后,会直接从join方法中返回。

其他方式

至此,我们了解到了join方法的原理,不过join我们很少用到,在主线程等待子线程执行结束再执行的场景下,有更好更优雅的方法,可以参考我的另外一篇博客面试官:如何让主线程等待所有的子线程执行结束之后再执行?我懵了

标签: #vbnetthreadjoin