jdk1.5前用
Thread
jdk1.5出现了
ExecuteService
从接口的使用便利程度上远胜Thread,更不用说在java.util.concurrent包中的各种线程池,Future类和Lock等便利工具,由于ExecuteService是接口,面向接口编程的实现只需要实现该接口即可。
{
private ExecuteService executeService;
private int parallism;
// 构造函数
public xxxx(){
parallism = Runtime.getRuntime().avaliableProcessors();// 线程池个数,默认cpu的核心数
executeService = Executors.newFixedPoolThread(parallism);
}
//处理计算任务的线程
private static class SumTask implements Callable<Long> {
private long[] numbers;
private int from;
private int to;
public SumTask(long[] numbers, int from, int to) {
this.numbers = numbers;
this.from = from;
this.to = to;
}
@Override
public Long call() {
long total = 0;
for (int i = from; i <= to; i++) {
total += numbers[i];
}
return total;
}
}
@Override
public long sumUp(long[] numbers) {
List<Future<Long>> results = new ArrayList<>();
// 把任务分解为 n 份,交给 n 个线程处理 4核心 就等分成4份呗
// 然后把每一份都扔个一个SumTask线程 进行处理
int part = numbers.length / parallism;
for (int i = 0; i < parallism; i++) {
int from = i * part; //开始位置
int to = (i == parallism - 1) ? numbers.length - 1 : (i + 1) * part - 1; //结束位置
//扔给线程池计算
results.add(pool.submit(new SumTask(numbers, from, to)));
}
// 把每个线程的结果相加,得到最终结果 get()方法 是阻塞的
// 优化方案:可以采用CompletableFuture来优化 JDK1.8的新特性
long total = 0L;
for (Future<Long> f : results) {
try {
total += f.get();
} catch (Exception ignore) {
}
}
return total;
}
}
ForkJoinPool
是ExecuteService的补充,不是替代。
ExecuteSerivice是所有核心线程共享一个队列,当一个任务执行完成就会自动从队列中取出任务执行,而ForkJoinPool强调的是工作窃取算法。就是线程任务执行完成后会寻找未被执行的任务并且执行任务。
区别在于ForkJoinPool的分而治之的方法。ForkJoinPool会把任务递归细分成许多更小的任务,并且把任务的结果返回执行。而ExecuteService也可以实现这个方法,只是写的代码更麻烦一些。而ForkJoinPool的fork()方法为执行线程,join()为返回的结果。
{
private ForkJoinPool pool ;
//执行任务RecursiveTask:有返回值 RecursiveAction:无返回值
private static class SumTask extends RecursiveTask<Long> {
private long[] numbers;
private int from;
private int to;
public SumTask(long[] numbers, int from, int to) {
this.numbers = numbers;
this.from = from;
this.to = to;
}
//此方法为ForkJoin的核心方法:对任务进行拆分 拆分的好坏决定了效率的高低
@Override
protected Long compute() {
// 当需要计算的数字个数小于6时,直接采用for loop方式计算结果
if (to - from < 6) {
long total = 0;
for (int i = from; i <= to; i++) {
total += numbers[i];
}
return total;
} else { // 否则,把任务一分为二,递归拆分(注意此处有递归)到底拆分成多少分 需要根据具体情况而定
int middle = (from + to) / 2;
SumTask taskLeft = new SumTask(numbers, from, middle);
SumTask taskRight = new SumTask(numbers, middle + 1, to);
taskLeft.fork();
taskRight.fork();
return taskLeft.join() + taskRight.join();
}
}
}
// 构造函数
public xxxx(){
pool = new ForkJoinPool();
}
public long sumUp(long[] numbers) {
Long result = pool.invoke(new SumTask(numbers, 0, numbers.length - 1));
pool.shutdown();
return result;
}
}
用ForkJoinPool的场景是 :计算数量密集型的任务。