Java并发编程之join源码分析

113 阅读2分钟

a6765b0539a8b0716368bf4060c21378.jpeg

join方法源码

join方法的作用是让当前线程等待另一个线程执行完,主要应用于多个线程需要按照指定顺序执行的场景。

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

查看Thread类中join()方法发现实际调用的是重载方法join(long millis)并且参数是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);
        }
    } else {  // ②
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

首先对其中的几个变量进行解释:

  • base:记录开始等待的时间
  • now:已经等待的时间
  • millis:一共要等待的时间
  • delay:剩余要等待的时间
  • isAlive():查看线程是否存活
  • wait():当前线程进行等待

wait方法的影响,我开始一直认为isAlive方法是查看当前线程的存活状态,所以逻辑上就一直搞不通。后来我理解的是t1.isAlive()是查看t1线程是否存活,而不是当前线程

情景一:main线程调用t1.join()

t1.join()实际调用的是t1.join(0),进入方法后会执行到处的if语句中,这里的while循环换一种写法就容易理解了

while(t1.isAlive()) {
    t1.wait(0);
}

也就是说去查看t1线程是否存活,如果存活的话就让当前main线程一直等待,所以等t1线程执行完之后t1.isAlive()结果为falsemain线程就不再等待可以继续执行了

情景二:main线程调用t1.join(1000)

millis等于1000时,进入②处的else语句,同样循环去判断t1线程是否存活,存活的话先计算还需要等待的时间delay,如果delay<0就跳出循环;如果delay>0,就继续进行等待,然后更新当前已经等待的时间now

所以当t1执行完或者等待的时间结束main线程都会继续执行不再继续等待。

注意到join(long millis)方法前面有synchronized,所以可以调用wait方法。

如果两个线程都调用t1.join(),有一个线程就会因为获取不到锁进行阻塞。

面试题分享

waitsleep有什么区别?

waitsleep方法都能够使当前线程进行等待,区别主要有以下几点

  • wait方法必须在获取锁之后调用,sleep方法可以在任何地方调用;
  • wait方法会释放锁,让出CPU的执行权;sleep不会让出CPU的执行权;
  • 调用wait方会使线程进入WAITIING状态,sleep方法使线程进入TIMED_WAITING状态;
  • 正因为wait方法需要加锁,而加锁针对的是对象,所以wait方法定义在了Object中;而sleep不需要加锁,只针对当前线程,所以定义在了Thread类中。

能力一般,水平有限,不足之处还望指正,我会好好改的