Java 中 Thread 的 join 方法

Java Thread 面试 大约 5209 字

介绍

t1.join()暂停的是当前调用join方法的线程,如在main线程里调用,则暂停主线程,如在子线程t2里调用,则暂停子线程t2,直到t1线程执行完成。

前提必须是t1线程是运行中,且未结束运行的。

示例代码

public class ThreadJoinDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(LocalDateTime.now() + " t1 is running, group#" + Thread.currentThread().getThreadGroup());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            try {
                //t1调用join方法, t2会等待t1运行完之后才会开始执行后续代码
                t1.join();
                System.out.println(LocalDateTime.now() + " t2 is running, group#" + Thread.currentThread().getThreadGroup());
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2");

        Thread t3 = new Thread(() -> {
            try {
                //t2调用join方法, t3会等待t2运行完之后才会开始执行后续代码
                t2.join();
                System.out.println(LocalDateTime.now() + " t3 is running, group#" + Thread.currentThread().getThreadGroup());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t3");

        //依次启动3个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

输出:

2021-04-06T16:09:06.087 t1 is running, group#java.lang.ThreadGroup[name=main,maxpri=10]
2021-04-06T16:09:09.102 t2 is running, group#java.lang.ThreadGroup[name=main,maxpri=10]
2021-04-06T16:09:10.103 t3 is running, group#java.lang.ThreadGroup[name=main,maxpri=10]

源码分析

join()在调用时wait()等待线程执行完成后,底层C++代码在线程结束后自动调用JavaThread::exit方法,唤醒线程。

public class Thread implements Runnable {

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
        ...
        Thread parent = currentThread(); // new 该线程的线程,案例中是 main 线程
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            // security 为 null
            if (security != null) {
                g = security.getThreadGroup();
            }

            // main 线程的用户组是 main
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        ...
    }

    // 阻塞直到线程运行结束
    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); // Object 的 wait 方法,阻塞在此
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     *
     * 测试线程是否还存活。线程如果已启动或者未结束退出则还是存活状态。
     */
    public final native boolean isAlive();

    // 线程所属的组
    private ThreadGroup group;

    /**
     * This method is called by the system to give a Thread
     * a chance to clean up before it actually exits.
     * 
     * 此方法是系统调用,在线程真正退出前给它一个机会去做清理工作
     */
    private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }

        target = null;
        // 主动置为 null 加快内存释放
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

}

public class ThreadGroup implements Thread.UncaughtExceptionHandler {
    void threadTerminated(Thread t) {
        synchronized (this) {
            remove(t);

            // 如果线程组中的线程为 0,则 notifyAll 唤醒所有线程
            if (nthreads == 0) {
                notifyAll();
            }
            if (daemon && (nthreads == 0) &&
                (nUnstartedThreads == 0) && (ngroups == 0))
            {
                destroy();
            }
        }
    }
}

线程在结束运行退出时,底层C++调用线程notify_all唤醒该线程上wait的对象。

源码路径:openjdk\hotspot\src\share\vm\runtime\thread.cpp

#thread.cpp
//线程结束,调用该方法
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
  ...
  ensure_join(this);
  ...
}

static void ensure_join(JavaThread* thread) {
  ...
  ObjectLocker lock(threadObj, thread);
  ...
  //唤醒等待在thread对象上的线程
  lock.notify_all(thread);
  ...
}

#ObjectSynchronizer.hpp
void notify_all(TRAPS)      { ObjectSynchronizer::notifyall(_obj,    CHECK); }
阅读 67 · 发布于 2021-04-12

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批