一、前言
按照惯例,我们在讲一个知识点之前习惯性的提出一个业务场景的应用来帮助理解,今天咱们就通过一个渣男的案例来讲讲join的相关特性:
土木三班陈同学是一个很有名气的渣男,最近陈同学刚刚跟女朋友分了手,处于空窗期的他打开了微信附近的人,挑了个他觉得长得还不错的姑娘发起了搭讪请求。
public class ChatUp {
// 被搭讪人是否回复
public volatile static boolean flag = false;
// 微信昵称
public static String userName = "土木三班陈同学";
public static void main(String[] args) throws InterruptedException {
// 这天陈同学闲得无聊 打开附近的人进行搭讪
Thread thread = new Thread(() -> {
try {
String msg = Thread.currentThread().getName() + "同学,你好像我认识的一个人!";
// 陈同学发起搭讪
say(userName, msg);
Thread.sleep(1_000);
// 蛋蛋同学回复
say(Thread.currentThread().getName(), "像谁?");
flag = true;
} catch (InterruptedException e) {
}
}, "蛋蛋");
thread.start();
// 这里小小的休眠下 保证控制台输出顺序
Thread.sleep(5l);
// 判断是否回复了陈同学
if (flag) {
say(userName, "我的心上人!");
} else {
System.err.println("陈同学没有等到回复,于是就去睡觉了!");
}
}
/**
* @value user 谈话发起方
* @value msg 谈话内容
*/
public static void say(String user, String msg) {
System.out.println(user + " : " + msg);
}
}
这里我们看下程序的运行结果:
由于我们新建线程thread 来完成搭讪的任务,陈同学没有等待这个任务返回结果就去睡觉了,错失了一段大好姻缘,这样显然是不行的,所以我们这里需要优化一下上面的程序,让陈同学得到thread 线程的回复再去睡觉。那么我们应该怎么实现呢?这就是我们今天要讲的知识点join
二、join方法
1. join的Api简介
学习一个新的方法,肯定是要对照官方文档才更清晰,
其实在这里涉及join方法的描述概括起来就只有两点:
- join方法调用后当前线程会等待目标前程死亡后才会继续运行
- 直接调用join() 方法等同于调用 join(0)
这里我们对上面的Demo做一点更改,来观察下join的现象:
public class ChatUp {
// 被搭讪人是否回复
public volatile static boolean flag = false;
// 微信昵称
public static String userName = "土木三班陈同学";
public static void main(String[] args) throws InterruptedException {
// 这天陈同学闲得无聊 打开附近的人进行搭讪
Thread thread = new Thread(() -> {
try {
String msg = Thread.currentThread().getName() + "同学,你好像我认识的一个人!";
// 陈同学发起搭讪
say(userName, msg);
Thread.sleep(1_000);
// 蛋蛋同学回复
say(Thread.currentThread().getName(), "像谁?");
flag = true;
System.err.println(Thread.currentThread().getName()+"线程已经执行完毕");
} catch (InterruptedException e) {
}
}, "蛋蛋");
thread.start();
thread.join();
// 这里小小的休眠下 保证控制台输出顺序
Thread.sleep(5l);
// 判断是否回复了陈同学
if (flag) {
say(userName, "我的心上人!");
} else {
System.err.println("陈同学没有等到回复,于是就去睡觉了!");
}
System.err.println(Thread.currentThread().getName()+"线程已经执行完毕");
}
/**
* @value user 谈话发起方
* @value msg 谈话内容
*/
public static void say(String user, String msg) {
System.out.println(user + " : " + msg);
}
}
观察下结果:
这里我们可以观察到,main线程是等待蛋蛋线程运行后并且拿到了被蛋蛋线程修改的flag参数的最新值。证明了调用join方法可以让当前线程等待目标线程运行结束后再继续运行。
2. join方法的实现
为了摸清楚join方法的实现原理,我们需要打开join对应的源码:
这里我们惊喜的发现其实join方法的底层实现很简单,就是通过调用wait方法的有参形式来实现让调用线程进行等待。当然,既然他是通过wait方式实现,那就代表他也拥有以下特性:
1. 调用wait方法会立即释放当前线程持有的锁
2. 当调用join后线程处于等待状态时,调用interrupt方法会让当前线程抛出interruptException异常
3.总结
join方法主要应用在当前线程依赖于目标线程执行结果的情况下调用,其具备调用后会立即释放锁的特性,这里还有一点需要注意,很多人可能会疑惑既然他底层调用了wait方法来实现暂停,可是并没有找到其他线程调用notify方法进行唤醒。其实关于这点我们可以打开JDK的源码,当中有这么一段描述:
当线程结束时,线程会调用exit方法退出,exit方法内部又调用了ensure_join方法,ensure_join方法里面调用了lock。notify_all(thread)来实现唤醒当前还在等待的线程。
至此今天的内容就全部结束了,喜欢的小伙伴可以一键三连!
祝三连的小伙伴都可以升职加薪走上人生巅峰!