化繁为简,分而治之。利用递归的分解和合并,直到任务小到可以接受的程度。
1. Future任务机制和FutureTask
Future类对具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。
boolean cancel(boolean mayInterruptIfRunning):取消任务,参数表示是否允许取消正在执行却没有执行完毕的任务。
boolean isCancelled() :任务是否取消成功,如果在完成之前取消则为true。
boolean isDone():任务是否已经完成。
Object get(): 获取执行结果,会产生阻塞,等到任务执行完毕才返回。
Object get(long timeout, TimeUnit unit): 在指定时间内获取执行结果,超出时间返回为null。
Future功能:判断任务是否完成;能够中断任务;获取任务执行结果。
Future只是接口无法用来创建对象使用,因此有了FutureTask。
构造器内容:
public FutureTask(Runnable runnable, V result):返回给定的结果
public FutureTask(Callable callable) :执行给定的Callable。
2. Fork/Join框架
概念:java7提供的用于并行执行任务的框架 ,将大任务分割成若干小任务,最终汇总到每个小任务结果后得到大任务结果的框架。
代码实例:执行1+2+3+4的结果
public class ForkJoinTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
CountTask countTask = new CountTask(1, 5);
ForkJoinTask<Integer> result = forkJoinPool.submit(countTask);
System.out.println("result = " + result.get());
}
static class CountTask extends RecursiveTask<Integer> {
private static int splitSize = 2;
private int start, end;
public CountTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
//如果任务不需要再拆分就开始计算
boolean canCompute = (end - start) <= splitSize;
if (canCompute) {
for (int i = start; i <= end; i++) {
sum = sum + i;
}
} else {
//拆分成两个子任务
int muddle = (start + end) / 2;
CountTask firstTask = new CountTask(start, muddle);
CountTask secondTask = new CountTask(muddle + 1, end);
//开始执行
firstTask.fork();
secondTask.fork();
//获得子任务结果,得不到记过线程不会继续往下执行
Integer firstResult = firstTask.join();
Integer secondResult = secondTask.join();
//合并两个子task执行结果
sum = firstResult + secondResult;
}
return sum;
}
}
}
3. ForkJoin的JDK家族
3.1 ForkJoinTask
实现Future 的另一种有结果的实现方法,比Future多了两个重要的方法,
fork():创建新任务;join()计算完成后返回结果。
RecursiveAction:继承ForkJoinTask,用于没有返回结果的任务。
RecursiveTask:继承ForkJoinTask,用于有返回结果的任务。
ForkJoinPool:实现Executor接口。
ForkJoinPool提供三个方法来调度子任务:
execute:异步执行指定任务;
invoke:执行指定的任务,等待完成,返回结果;
submit:异步执行指定任务,立即返回一个Future对象。
4. Fork/Join框架实现原理
利用工作窃取算法,使空闲线程主动分担从其他线程分解出来的子任务。使用双端队列,窃取线程从尾部,被窃取线程从头部执行任务。
缺点:当有一个任务时,消耗更多系统资源创建多个线程和多个双端队列。
5. 异常处理机制
通过isCompletedAbnormaly()方法来检查任务是否抛出异常,通过ForkJoinTask的getException方法获异常。方法返回Throwable对象,如果任务取消,返回CancellationException。没有任务完成或者没有抛出异常则返回null。
6. Fork/Join模式优缺点及实际应用场景
优点:更方便利用多核平台计算能力,解决死锁问题。仅仅关注如何划分任务和组合中间结果就可以。对于树形结构类型的数据处理和便利非常适合。
7. 合理设计和配置线程池
最大限度发挥单台物理server机器的最大并发量(线程数),但不至于系统崩溃。需要知道本台server最大可响应的并发量是多少,极限并发量是多少,可以利用压测。