Java Fork/Join框架和ExecutorService的区别

Java Fork/Join框架和ExecutorService的区别

在Java的并发编程领域中,开发人员有众多选择。Fork/Join框架和ExecutorService是其中备受欢迎的两种选择。虽然这两种解决方案都擅长将操作并行化,但它们在如何根据使用场景的不同要求进行结构化方面存在差异。通过本文对每个框架的语法特性以及实际编码示例的洞察,用户可以更好地理解它们在比较中的优势所在。

语法

Fork/Join框架

class ForkJoinTask<V> extends Object

ExecutorService

interface ExecutorService extends Executor

语法解释

Fork/Join框架是围绕ForkJoinTask类构建的,该类表示可以分解为较小子任务的任务。参加此程序使您有机会了解任务的递归分解以及如何同时执行这些任务。此外,通过使用在Executor接口之上构建的ExecutorService接口,您可以以更优秀的方式执行异步任务。它管理一个线程池并处理任务的提交和执行。

方法1:Fork/Join框架

步骤

  • 定义一个ForkJoinTask子类,表示要执行的任务。

  • 在子类中实现compute()方法,将任务分为较小的子任务并调用它们的执行。

  • 将子任务的结果合并以产生最终结果。

    方法一完整的可执行代码

示例

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

class MyTask extends RecursiveTask {
   private static final int THRESHOLD = 10;

   private int[] array;
   private int start;
   private int end;

   public MyTask(int[] array, int start, int end) {
      this.array = array;
      this.start = start;
      this.end = end;
   }

   @Override
   protected Integer compute() {
      if (end - start <= THRESHOLD) {
         // Perform the computation directly
         int sum = 0;
         for (int i = start; i < end; i++) {
            sum += array[i];
         }
         return sum;
      } else {
         // Divide the task into smaller subtasks
         int mid = start + (end - start) / 2;
         MyTask leftTask = new MyTask(array, start, mid);
         MyTask rightTask = new MyTask(array, mid, end);

         // Fork the subtasks
         leftTask.fork();
         rightTask.fork();

         // Combine the results
         int leftResult = leftTask.join();
         int rightResult = rightTask.join();

         // Return the final result
         return leftResult + rightResult;
      }
   }
}

public class ForkJoinExample {
   public static void main(String[] args) {
      int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      MyTask task = new MyTask(array, 0, array.length);

      // Create a ForkJoinPool and invoke the task
      ForkJoinPool pool = new ForkJoinPool();
      int result = pool.invoke(task);

      System.out.println("Result: " + result);
   }
}

输出

Result: 55

方法1的代码解释

我们的方法是创建一个专门的类别MyTask,它派生自RecursiveTask,这样我们就可以运行计算并接收输出结果。为了实现这个目标,我们修改了compute()方法,当任务超过我们设定的限制时,将其分割成较小的子任务。然后,这些较小的子任务被forked,并且将它们的结果汇集在一起以产生最终的结果。

方法2:ExecutorService

步骤

  • 使用Executors类创建一个ExecutorService实例。

  • 定义一个Callable或Runnable实现,代表要执行的任务。

  • 将任务提交给ExecutorService进行执行。

  • 如果需要,获取结果。

    方法2的完整可执行代码

示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class MyTask implements Callable<Integer> {
   private int[] array;
   private int start;
   private int end;

   public MyTask(int[] array, int start, int end) {
      this.array = array;
      this.start = start;
      this.end = end;
   }

   @Override
   public Integer call() throws Exception {
      int sum = 0;
      for (int i = start; i < end; i++) {
         sum += array[i];
      }
      return sum;
   }
}

public class ExecutorServiceExample {
   public static void main(String[] args) throws Exception {
      int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

      ExecutorService executorService = Executors.newFixedThreadPool(2);

      MyTask task1 = new MyTask(array, 0, 5);
      MyTask task2 = new MyTask(array, 5, 10);

      Future<Integer> future1 = executorService.submit(task1);
      Future<Integer> future2 = executorService.submit(task2);

      int result = future1.get() + future2.get();

      executorService.shutdown();

      System.out.println("Result: " + result);
   }
}

输出

Result: 55

第二种方法的代码解释

在这种方法中,我们使用Executors类创建一个ExecutorService,它提供了一个包含两个线程的固定线程池。我们定义了一个实现了Callable接口的MyTask类,表示要执行的任务。call()方法执行计算并返回结果。我们使用submit()方法将两个MyTask的实例提交给ExecutorService,该方法返回表示计算结果的Future对象。最后,我们从Future对象中获取结果并计算最终结果。

方面 Fork/Join 框架 ExecutorService
语法 class ForkJoinTask extends Object interface ExecutorService extends Executor
设计目的 递归分解任务 异步任务执行和线程管理
粒度 最适合细粒度任务 适用于细粒度和粗粒度任务
任务依赖 隐式处理递归任务分解 需要显式提交任务
并行性 利用工作窃取算法进行负载平衡 管理线程池和任务执行
结果收集 结果以分层方式合并 通过 Future 对象获取结果
任务提交 在一个任务中进行递归分解 独立提交任务
控制 对线程管理的控制有限 对线程管理和执行有更大的控制
使用案例 分而治之算法,递归任务 并发执行独立任务

结论

总之,Java中的Fork/Join Framework和ExecutorService都提供了强大的并发编程机制。Fork/Join Framework专为递归分解任务而设计,特别适用于可以分解为子任务的问题。它通过利用多线程并合并结果来实现高效的并行执行。另一方面,ExecutorService提供了更通用的异步任务执行方法,提供线程管理和对执行环境的控制。它非常适合同时执行独立任务并在需要时获取结果。通过了解这些框架的区别和特点,开发者可以根据具体要求选择最合适的方法,从而实现高效和可扩展的并发Java程序。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程