码农在囧途
不可否认的是,在这个圆滑当道的时代,如果一个人太过于正直,刚正不阿,那么可能会四处碰壁,而那些世故的机会主义往往如鱼得水,我们面对 这样一个社会规则,我觉得我们可以适当收起自己的锐利,做到知圆滑而不圆滑,知世故而不世故,应该保持一颗年轻的心态,而不是随着年龄的增长 而变得油腻,圆滑,世故。
join()使用场景
当一个方法中调用了多个接口时,为了提升效率,我们通常会使用多线程进行异步调用,不过有一些场景我们需要对某几个接口的值进行汇总,然后再去调用其他的接口, 我们在统计一些报表时,往往会进行大量的运算,调用,然而它们之间会存在依赖,所以我们就需要使用相应的机制来保证执行的顺序,实现方式有很多,Java中我们可以 使用join()
,闭锁CountDownLatch()
,信号量Semaphore()
等,今天我们先从基础的join()
说起。
比如调用Rpc接口Api1和Api2成功后,我们用这两个接口的返回值进行处理,然后去调用Api5,那么此时我们就需要在调用Api5之前确保Api1和Api2执行完成。
如上图,Api1和Api2执行完成后再轮到Api5执行,那么我们就可以使用join()方法来做。
join()示例
使用join()后,会阻塞使用join的线程,直到任务执行完成后才会返回,如下,主线程启动了线程1和线程2后,使用了join()将两个线程进行阻塞,等待thread1和thread2 执行完任务后才继续往下执行doIt()方法。
代码语言:javascript复制public class ThreadOfJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(2000);
System.out.println("线程1执行完毕");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程2执行完毕");
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
doIt();
}
public static void doIt(){
System.out.println("线程1和线程2执行完成后才到我执行");
}
}
输出
线程2执行完毕 线程1执行完毕 线程1和线程2执行完成后才到我执行
从上面的输出可以看出线程使用join()可以阻塞线程,直到任务完成后才会向下执行。
join源码分析
调用join(millis)或join()后,首先会获取当前的时间戳base,如果传递过来的时间参数millis为0并且线程处于存活状态,那么线程将会一直挂起,直到任务完成, 如果millis大于0,就会计算出线程挂起的时间,然后循环判断挂起的时间当挂起的时间<=0,那么代表当前线程任务已经完成,结束挂起,join()里面挂起线程使用的 是Object中的await()方法。
代码语言:javascript复制public class Thread implements Runnable{
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");
}
//传过来的时间参数如果为0
if (millis == 0) {
//如果线程还是存活状态,那么将会一直挂起
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
//delay时延迟时间
long delay = millis - now;
//延迟时间小于0,则证明线程结束挂起
if (delay <= 0) {
break;
}
//线程挂起
wait(delay);
//计算时间间隔
now = System.currentTimeMillis() - base;
}
}
}
}
今天的分享就到这里,感谢你观看,下期见。