简单理解下Thread.join()方法

线程A中调用线程B的join方法:线程A会阻塞直到线程B执行完毕或者当B线程被中断,A线程才继续执行。

简单来说:强制让一个线程阻塞直到另一个线程执行完毕后再继续向下执行。

应用场景:一个线程的执行依赖于另一个线程的执行结果,如在主线程中对多个子线程执行的结果进行汇总,需要等待子线程执行完毕。

实现原理:

调用时如果不带参数,默认调用join(0),该方法响应中断,如果被中断了,抛出中断异常,并且当前线程的中断标志会被清除。

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

可以使用带参的方法,join(long millis),设置等待时间,单位是毫秒。

具体方法:

public final synchronized void join(long millis) throws InterruptedException { //join方法是同步方法!通过wait(0)和wait(millis)实现。
        long base = System.currentTimeMillis(); //返回以毫秒为单位的当前时间,作为计算的开始时间
        long now = 0; //已等待时间

        if (millis < 0) { //等待时间小于0,参数错误,抛出异常
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) { //默认不带时间参数的调用
            while (isAlive()) { //一直在循环中调用wait(0)方法,wait(0)会让出cup的执行权。
                wait(0);
            }
        } else {                                                                              
            while (isAlive()) {  //带参数的调用,依然在while(isAlive)中,不过多了等待时间计算,等待了指定时间后退出。
                long delay = millis - now;  //剩余等待时间 等于 等待时间减去已等待时间
                if (delay <= 0) { //剩余等待时间小于等于0,退出等待
                    break;
                }
                wait(delay);  //等待
                now = System.currentTimeMillis() - base;//更新已等待时间,已等待时间等于当前时间减去开始时间
            }
        }
    }

 因此join方法本质是通过wait(timeout)实现等待阻塞的,在等待前会先获取线程对象的锁。等待条件满足时,由jvm来唤醒。

 并且方法的源码注释中说,应用程序不应该使用wait,notify,notifyAll在该线程的实例上,这应该是可能会让等待线程提前被唤醒或继续等待。

而wait()方法是怎么实现的呢?这涉及了synchronized的锁对象的监视器了锁,监视器锁(ObjectMonitor)由C++实现,有两个队列,一个同步队列(EntryList),一个等待队列(WaitSet),线程在获取了锁对象的监视器锁后,调用wait()方法,就会进入等待队列进行等待,直到被其他线程唤醒(notify,notifyAll)。

 在jdk1.5后之的并发包中提供的CountDownLatch也可以实现join的方法 ,并且比join的功能更多,下次再说。


posted on 2020-10-23 11:23  菜鸟向前冲冲冲  阅读(239)  评论(0编辑  收藏  举报