Discussion on Java thread ES1en. join method analysis

  • 2020-12-21 18:02:53
  • OfStack

join literally means to add, so let's look at the interpretation and implementation of the join method.


/**
   * Waits for this thread to die.
   *  The caller thread (call join Method thread) performs a wait operation until the called thread ( join The thread to which the method belongs terminates and is woken up 
   * <p> An invocation of this method behaves in exactly the same
   * way as the invocation
   *
   *
   * @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() throws InterruptedException {
    join(0);
  }

Here join is called


/**
   * Waits at most {@code millis} milliseconds for this thread to
   * die. A timeout of {@code 0} means to wait forever.
   *  Waiting for the thread to finish executing, or the specified maximum wait time is up, the caller thread is woken up again if the maximum wait time is 0 Can only be woken up after the thread has finished executing. 
   * <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.
   *
   * 
   */
  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;
      }
    }
  }

As you can see, the join method itself implements the wait through the wait method, which determines that if the thread is still running, it will continue to wait, and if the specified time is up or the thread has finished running, the code will continue to execute downwards, and the calling thread can execute the following logic.

But here you don't see where the notify or notifyAll method is called. If it is not called, then the caller thread will wait for 1, so where is the method that wakes it up? It turns out that at the end of the thread, the java virtual machine executes the thread's local exit method,


// Thread exit function: 
void JavaThread::exit(bool destroy_vm, ExitType exit_type)  { 
...
// This is going to deal with join Related destruction logic 
ensure_join(this);
...
 } 
// To deal with join Related destruction logic 
  static void ensure_join(JavaThread* thread) {
   Handle threadObj(thread, thread->threadObj());

   ObjectLocker lock(threadObj, thread);

   thread->clear_pending_exception();

   java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);

   java_lang_Thread::set_thread(threadObj(), NULL);

   // I'm going to call notifyAll Method to wake up the waiting thread 
   lock.notify_all(thread);

   thread->clear_pending_exception();
  }

So you know when the thread will wake up. Let me write an example to see what happens.


public class JoinTest {
  
  public static void main(String[] args) {
    
    ThreadBoy boy = new ThreadBoy();
    boy.start();
    
  }
  
  static class ThreadBoy extends Thread{
    @Override
    public void run() {
      
      System.out.println(" The boy and girl are going out shopping ");
      
      ThreadGirl girl = new ThreadGirl();
      girl.start();
      
      try {
        girl.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println(" The boy and the girl began to go shopping ");
    }
  }
  
  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;
      
      System.out.println(" The girl began to put on her makeup , The boy is waiting... ");
      
      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println(" Girl makeup done! And time consuming " + time);
      
    }
  }
  
}

The execution result is:

The boy and girl are going out shopping
The girl starts to make up, the boy is waiting...
Girl makeup done! , 5000
The boy and the girl began to go shopping

Boys and girls are ready to go shopping, the girl to make up, after the girl finished makeup, then 1 to go shopping.

What about join(time)?


public class JoinTest {
  
  public static void main(String[] args) {
    
    ThreadBoy boy = new ThreadBoy();
    boy.start();
    
  }
  
  static class ThreadBoy extends Thread{
    @Override
    public void run() {
      
      System.out.println(" The boy and girl are going out shopping ");
      
      ThreadGirl girl = new ThreadGirl();
      girl.start();
      
      int time = 2000;
      try {
        girl.join(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println(" The boy etc. " + time + ",  Don't want to wait any longer, go shopping ");
    }
  }
  
  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;
      
      System.out.println(" The girl began to put on her makeup , The boy is waiting... ");
      
      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println(" Girl makeup done! And time consuming " + time);
      
    }
  }
  
}

Here we simply replace the join method with the join(time) method. The description is changed a little bit, and the printed result is:

The boy and girl are going out shopping
The girl starts to make up, the boy is waiting...
The boy didn't want to wait any longer after waiting for 2000. He went shopping
Girl makeup done! , 5000

The boy waited for the time time in join(time). If after the arrival of the time time, the thread where the girl was not finished, he would not wait and continue to execute the following logic, that is, he would not wait for the girl and go shopping by himself.

It can be seen that the join method is to facilitate the synchronous execution of two threads. Thread 1 executes, and when it encounters thread 2, it waits for thread 2 to execute, and then continues to execute thread 1.


Related articles: