zoukankan      html  css  js  c++  java
  • Java里面Join(),为什么等待的是主线程,而不是当前子线程?

    1.问题描述

    ​ 当我们想要一个线程插队执行的时候,我们可能会使用到thread.join();。这个会让子线程先于主线程执行完毕,然后才开始执行子线程。但是仔细一想,发现这个明明调用的是子线程的join()方法,按道理应该子线程等待执行才是,为什么反而是主线程等待了呢?相关的示例代码如下:

    public static void main(String[] args) throws InterruptedException {
    
            Thread thread = new Thread(() -> {
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程开始执行...");
    
            });
    
            thread.start();
            thread.join();
            System.out.println("主线程执行...");
    
        }
    

    输出:

    子线程开始执行...
    主线程执行...
    

    2.查看源码说明

    带着这个主线程等待执行的疑惑来一起看下join的源码,如下所示:

    /**
         * 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() throws InterruptedException {
            join(0);
        }
    

    谁等待谁终止?源码中注释说明的是等待这个线程终止,那就是等待调用Join()的线程终止,再继续往下看:

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

    从Join的源码中,我们可以看到它使用了while (isAlive()) 循环判断线程的存活状态,满足就调用wait方法,当有设置时长的时候会根据时长来进行等待。注意按照程序的执行顺序,我们这里是主线程调用的Thread的Join方法,所以是判断子线程的存活状态,满足则让子线程执行,主线程来等待。

    wait 等待方法是让线程进入等待队列,使用方法是 obj.wait(); 这样当前线程就会暂停运行,并且进入obj的等待队列中,称作“线程正在obj上等待”。可以把子线程t理解为一个普通的obj对象,调用t的wait()方法,实际上就是主线程(main线程)在childThread对象的队列上等待,可以转换为如下写法进一步理解:

    /**
     * 主线程
     */
    public class MainTest {
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println("主线程开始执行...");
    
            Thread childThread = new ChildThread();
            childThread.start();
            while (childThread.isAlive()) {
                synchronized (childThread) {
                    // 此时主线程进行等待并释放锁资源 使子线程有得以运行的机会
                    childThread.wait();
                }
            }
    
            System.out.println("主线程执行结束...");
        }
    }
    
    /**
     * 子线程
     */
    class ChildThread extends Thread {
        public ChildThread() {
        }
    
        @Override
        public void run() {
            System.out.println("子线程开始执行...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程执行结束...");
            synchronized (this) {
                // 唤醒等待的主线程
                this.notifyAll();
            }
        }
    
    
    }
    
    

    输出:

    主线程开始执行...
    主线程执行结束...
    子线程开始执行...
    子线程执行结束...
    

    另外需要强调的一点:使用Join方法让主线程等待后,调用完wait方法后,JVM底层会隐式的调用notifyAll方法来唤醒主线程,使其得以继续往下执行。

  • 相关阅读:
    Beta冲刺(4/4)
    2019 SDN上机第7次作业
    Beta冲刺(3/4)
    Beta冲刺(2/4)
    机器学习第二次作业
    机器学习第一次作业
    软工实践个人总结
    第04组 Beta版本演示
    第04组 Beta冲刺(5/5)
    第04组 Beta冲刺(4/5)
  • 原文地址:https://www.cnblogs.com/charlypage/p/14383195.html
Copyright © 2011-2022 走看看