一、先看应用场景:
查询航班信息,你的APP是没有这些实时数据的,当用户发起查询请求时,你需要到各大航空公司的接口获取信息,最后统一整理加工返回到APP客户端。
串行任务局部并行化处理,用户在APP客户端输入出发地"上海"和目的地"北京",服务器接收到这个请求后,先来验证用户的信息,然后到各大航空公司的接口查询信息,最后经过加工返回给客户端,每一个航空公司的接口不一样,返回的数据格式也不同,查询速度也存在差异,如果串行化处理,很明显客户需要等待很长时间,我们将每一个航空公司的查询都交给一个线程去工作,然后在他们结束工作之后统一对数据进行处理,可以极大的节约时间。
二、实现:
1.不管是Thread的run方法,还是Runnable接口,都是void返回类型,如果想要得到运行结果,需要自己定义一个返回的接口。因此设计一个接口,用于获取当前线程的数据。如下所示:
package com.jseeker.api;
import java.util.List;
public interface FightQuery {
List<String> get();
}
2.查询航班的任务,其实就是一个线程的子类,主要用于到各大航空公司获取数据。由于每个航空公司的查询时间不太一样,示例代码中用一个随机数来模拟不同的查询速度。线程定义如下所示:
package com.jseeker.api;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class FightQueryTask extends Thread implements FightQuery {
private final String origin;
private final String destination;
private final List<String> flightList = new ArrayList<>();
public FightQueryTask(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", getName(), origin, destination);
int randomVal = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(randomVal);
this.flightList.add(getName() + "-" + randomVal);
System.out.printf("The Fight:%s list query successful\n", getName());
} catch (InterruptedException e) {
}
}
@Override
public List<String> get() {
return this.flightList;
}
}
3.接口和线程都定义好了,这一步就要写实现从上海到北京的航班查询了。示例代码如下:
package com.jseeker.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.util.stream.Collectors.toList;
public class FightQueryMain {
//航空公司
private static List<String> fightCompany = Arrays.asList("CSA", "CEA", "HNA");
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 original, String dest) {
final List<String> result = new ArrayList<>();
//创建查询航班信息的线程列表
List<FightQueryTask> tasks = fightCompany.stream()
.map(f -> createSearchTask(f, original, dest))
.collect(toList());
//分别启动这几个线程
tasks.forEach(Thread::start);
//分别调用每一个线程的join方法,阻塞当前线程
tasks.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
}
});
//在此之前,当前线程会阻塞住,获取每一个查询线程的结果,并且加入到result中
tasks.stream().map(FightQueryTask::get).forEach(result::addAll);
return result;
}
private static FightQueryTask createSearchTask(String fight, String original, String dest) {
return new FightQueryTask(fight, original, dest);
}
}
程序最终输出如下:
[CSA]-query from SH to BJ
[HNA]-query from SH to BJ
[CEA]-query from SH to BJ
The Fight:[CSA] list query successful
The Fight:[HNA] list query successful
The Fight:[CEA] list query successful
===============result===============
[CSA]-2
[CEA]-8
[HNA]-3
------------------------------------------------这是分割线------------------------------------------------
由于随机数的关系,每台电脑执行的效果可能不太一样,主要是用来模拟。利用学习到join知识解决实际问题。