Future&ForkJoin框架原理
Future:
Future是一种异步计算机制,可以在一个线程中提交一个任务,并在另一线程中的某个时候获取该任务的结果。Future提供了一个get方法,该方法会阻塞调用线程直到计算结果可用。Future还提供了isDone方法,用于检查计算是否已经完成。
示例代码:
ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(() -> { // 模拟长时间计算 Thread.sleep(3000); return 100; }); System.out.println("等待计算结果..."); while (!future.isDone()) { // 等待计算结果 } System.out.println("计算结果为:" + future.get()); executor.shutdown();
在上面的示例中,我们创建了一个ExecutorService,然后提交了一个Callable任务,该任务会休眠3秒钟,然后返回整数100。我们使用Future的get方法等待任务完成并获取结果。可以看到,在等待任务完成时,调用线程会一直被阻塞。
ForkJoin框架:
ForkJoin框架是一种并行计算框架,自JDK1.7引入以来,已经成为Java并发编程中的重要组成部分。ForkJoin框架通过将大任务拆分为小任务,然后在不同的线程中并行执行这些小任务,最终将所有的小任务结果合并得到最终结果。
ForkJoin框架中有两个重要的类:ForkJoinPool和ForkJoinTask。ForkJoinPool是一个特殊的线程池,用于执行ForkJoinTask。ForkJoinTask表示可以被拆分为更小任务的任务。ForkJoinTask有两种类型:RecursiveTask和RecursiveAction。RecursiveTask表示有返回值的任务,而RecursiveAction表示没有返回值的任务。
示例代码:
class SumTask extends RecursiveTask<Long> { private static final int THRESHOLD = 10000; private final int[] array; private final int start; private final int end; SumTask(int[] array, int start, int end) { this.array = array; this.start = start; this.end = end; } @Override protected Long compute() { if (end - start <= THRESHOLD) { long sum = 0; for (int i = start; i < end; i++) { sum += array[i]; } return sum; } else { int middle = (start + end) >>> 1; SumTask leftTask = new SumTask(array, start, middle); SumTask rightTask = new SumTask(array, middle, end); leftTask.fork(); rightTask.fork(); return leftTask.join() + rightTask.join(); } } } public static void main(String[] args) { int[] array = new int[100000]; for (int i = 0; i < array.length; i++) { array[i] = i + 1; } ForkJoinPool pool = new ForkJoinPool(); long startTime = System.currentTimeMillis(); long result = pool.invoke(new SumTask(array, 0, array.length)); long endTime = System.currentTimeMillis(); System.out.println("计算结果为:" + result); System.out.println("耗时:" + (endTime - startTime) + "毫秒"); pool.shutdown(); }
在上面的示例中,我们创建了一个有10万个元素的整数数组,然后创建了一个SumTask,该任务用于计算数组中所有元素的和。如果数组的长度小于等于阈值10000,我们直接计算结果并返回;否则,我们将数组拆分为两个子数组,分别创建任务计算子数组的和,并使用fork方法将任务提交到ForkJoinPool中。最后,我们使用join方法获取子任务的计算结果,并将结果进行合并。可以看到,在ForkJoin框架中,我们使用更少的代码实现了并行计算,同时也减少了很多线程同步的开销。
小故事
有一个农夫需要在他的田地里种植许多植物。为了尽快完成这项任务,他决定雇佣一些工人帮他。他将他的田地划分成许多小块,并为每个工人分配了一个小块来种植。每个工人都可以在他们的小块周围挖掘土壤,种下植物,并且记录他们的进度。
Future框架就像雇佣工人一样,它将任务划分成小部分并分配给不同的线程来处理。在我们的故事中,每个工人都在记录自己的进度,这就是Future的关键:每个线程都可以记录自己的进度并返回自己的结果给主线程。
那么,ForkJoin框架又是什么呢?它就像一个老板,会监督所有的工作并确定每个线程的进度。在我们的故事中,老板在收集工人的报告时会确定哪个区域已经完成,哪些工人需要帮助他们完成他们的任务。ForkJoin框架也是这样,它会监督所有任务的执行并决定是否需要fork新的任务,并将它们分配给其他线程来处理。
总的来说,Future和ForkJoin框架结合起来可以将大型任务分成小的部分,并同时利用多个线程来处理任务,从而提高系统的效率和响应速度。