1. Problem description

When we want a thread to jump the queue, we might use thread.join(); . This will cause the child thread to finish before the main thread, and then start executing the child thread. The join () method of the child thread should be waiting for execution, but the main thread should be waiting instead. Example code for this is as follows:

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("The child thread starts executing...");

        });

        thread.start();
        thread.join();
        System.out.println("Main thread execution...");

    }
Copy the code

Output:

The child thread starts executing... Main thread execution...Copy the code

2. View the source code

Let’s take a look at the source code of the join, as shown below:

/**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join(a) throws InterruptedException {
        join(0);
    }
Copy the code

Who waits for whom to terminate? Wait for this thread to terminate, that is, wait for the thread calling Join () to terminate, and then continue:

 /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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; }}}Copy the code

From the source code of Join, we can see that it uses the while (isAlive()) loop to determine the thread’s survival status, and then calls the wait method. When the time is set, it will wait according to the time. Note that in accordance with the execution order of the program, here is the Join method of Thread called by the main Thread, so it is to judge the survival status of the child Thread. If so, let the child Thread execute, and the main Thread wait.

Wait (obj.wait()); obj.wait(); The current thread will pause and enter obJ’s wait queue, called “thread waiting on OBJ”. Call t’s wait() method. The main thread is waiting on the queue of childThread.

/** ** main thread */
public class MainTest {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main thread starts executing...");

        Thread childThread = new ChildThread();
        childThread.start();
        while (childThread.isAlive()) {
            synchronized (childThread) {
                // The main thread waits and releases the lock resource so that the child thread can run
                childThread.wait();
            }
        }

        System.out.println("Main thread execution completed..."); }}/** * child thread */
class ChildThread extends Thread {
    public ChildThread(a) {}@Override
    public void run(a) {
        System.out.println("The child thread starts executing...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Child thread completes execution...");
        synchronized (this) {
            // Wake up the waiting main thread
            this.notifyAll(); }}}Copy the code

Output:

The main thread starts executing... Main thread execution complete... The child thread starts executing... The child thread completes...Copy the code

Note that after the wait method is called, the JVM implicitly calls notifyAll to wake up the main thread so that it can continue executing.