哈喽,小伙伴们,大家好。本章节继续分享join应用,希望大家有所收获。好了,废话不多说,进入正题。
需求:
假设一个软件,需要实现这样一个功能,主要用于查询航班信息,你的软件没有这些实时数据的,所以当用户发起查询请求时,你需要到各大航空公司的接口获取信息,最后统一整理加工返回到客户端。需求大概如下图:
分析:
其实功能很简单,就是我们输入起始地址,然后去查询每一家航空公司的航班信息,返回给我们,我们处理之后,再返回给客户端。就是先串行然后并行然后串行,即串行任务局部并行。
第一步:定义一个接口
/**
* 航班查询
*/
public interface FlightQuery {
List<String> get();// 航空公司名称-查询时长
}
因为不管是Thread 的 run 方法,还是 Runnable 接口,都是 void 返回类型,如果你想通过某个线程的运行得到结果,就需要自己定义一个返回接口。
第二步:定义一个任务实现类,去实现FlightQuery并且继承Thread类。
public class FlightQueryTask extends Thread implements FlightQuery {
private final String origin;// 出发地
private final String destination;// 目的地
private final List<String> flightList = new ArrayList<>();// 航班信息集合
public FlightQueryTask(String airline, String origin, String destination) {
super("[" + airline + "]");// 使用航空公司名称为线程命名
this.origin = origin;
this.destination = destination;
}
@Override
public void run() {
// 航空公司查询从出发地到目的地
System.out.printf("%s-query from %s to %s \n", this.getName(), origin, destination);
// 随机生成随机数模拟查询时长
int randomVal = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(randomVal);
// 将航空公司和查询时长添加至航班信息集合中
this.flightList.add(this.getName() + "-" + randomVal);
System.out.printf("The Flight:%s list query successful\n", this.getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public List<String> get() {
return this.flightList;// 返回航班查询信息
}
}
在代码里面,很多代码你可以不看,主要就是重执行单元run方法,因为逻辑都在run方法里面。然后run方法重点就是将航空公司和查询时长添加至航班信息集合中,最后通过get()方法获取航班信息。
第三步:编写具体实现类。
public class FlightQueryExample {
// APP 合作的航空公司
private static List<String> airlineList = Arrays.asList("南方航空", "东方航空", "海南航空");
public static void main(String[] args) {
List<String> results = search("SH", "BJ");
System.out.println("----------result----------");
results.forEach(System.out::println);
}
private static List<String> search(String origin, String destination) {
final List<String> result = new ArrayList<>();// 聚合所有航空公司查询结果的集合
// 创建查询航班信息的线程列表
List<FlightQueryTask> tasks = airlineList.stream()
.map(f -> new FlightQueryTask(f, origin, destination))
.collect(Collectors.toList());
// 启动线程
tasks.forEach(Thread::start);
// 调用每一个线程的 join 方法,阻塞当前线程
tasks.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 当前线程阻塞,获取每一个查询线程的结果,并且加入 result 中
tasks.stream().map(FlightQuery::get).forEach(result::addAll);
return result;
}
}
代码里面,相关的代码我已经注释,如果还是有不懂,建议去调试一下,还是不懂,给我留言,我给你讲。这个程序简单来说,就是主线程收到了search 请求之后, 交给了若干个査询线程分别进行工作,最后将每一个线程获取的航班数据进行统一的汇总。 由于每个航空公司的查询时间可能不一样,所以用了一个随机值来反应不同的查询速度,返回给客户端(打印到控制台)。
第四步:输出结果
总结:
如果我们去掉下面这段代码:则不能达到我们想要的效果。
// 调用每一个线程的 join 方法,阻塞当前线程
tasks.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
有兴趣的小伙伴可以尝试以下,好了,本次分享到此结束。下节内容我们将分享线程之间的通信,希望对你有帮助。