1、概念
1.1、用来做什么
1.2、意图梳理
1.3、使用场景
经典网关场景,查询多个系统数据,由于是接口调用,存在阻塞性,所以并不适合下边这种情况,但本教程会涉及到这个场景的实现来达到了解ForkJoinPool的使用,通过这个场景可以更清楚了解ForkJoinPool的使用:
通常的使用场景下边1.5会列出
1.4、实现思路
1.5、适用
2、代码
2.1、应用场景
一个方法中调用多个微服务获取数据:
上边这样写的问题是很大的,接口响应总时间大概为调用的各个微服务接口时间之和(还不包括本接口的其他逻辑处理),所以这样效率是非常低的。我们可以采用多线程应用来做这个事情。
先来了解forkjoinpool,它本质上还是一个线程池,默认的线程数量为cpu的核数,可以通过调用Executeors来获取,也可以直接用return 后边的代码进行创建:
forkjoinpool的使用方法与我们平时的线程池类似,也是用submit方法,不过除了可以穿runable和callable参数,多了可以传forkjointask参数的方法,由于这个类是一个抽象类,我们经常继承下边两个子抽象类做具体实现:
2.2、场景解决方法
前两个顺便提供了这个场景的常用方法,最后一个是为了介绍ForkJoinPool的使用才写的,实际中这样用到的并不多
2.2.1、利用FutureTask解决
这里顺便为了了解FutureTask的大致原理,自制了一个简单的futuretask,如不需要了解FutureTask原理可直接跳过:
import java.util.concurrent.*;
import java.util.concurrent.locks.LockSupport;
// 我们想一想,这个功能怎么实现
// (jdk本质,就是利用一些底层API,为开发人员提供便利)
public class NeteaseFutureTask<T> implements Runnable, Future {
// 获取 线程异步执行结果 的方式
Callable<T> callable; // 业务逻辑在callable里面
T result = null;
volatile String state = "NEW"; // task执行状态
LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();// 定义一个存储等待者的集合
public NeteaseFutureTask(Callable<T> callable) {
this.callable = callable;
}
@Override
public void run() {
try {
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
// result = exception
} finally {
state = "END";
}
// 唤醒等待者
Thread waiter = waiters.poll();
while (waiter != null) {
LockSupport.unpark(waiter);
waiter = waiters.poll(); // 继续取出队列中的等待者
}
}
// 返回结果,
@Override
public T get() {
if ("END".equals(state)) {
return result;
}
waiters.offer(Thread.currentThread()); // 加入到等待队列,线程不继续往下执行
while (!"END".equals(state)) {
LockSupport.park(); // 线程通信的知识点
}
// 如果没有结束,那么调用get方法的线程,就应该进入等待
return result;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;