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() 方法,將任務分解成更小的子任務並呼叫其執行。

  • 組合來自子任務的結果以生成最終結果。

方法 1 的完整可執行程式碼

示例

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<Integer>,以便我們可以執行計算並接收輸出。為了實現此目標,我們修改 compute() 方法,以便在任務超過我們的設定限制時將其劃分為較小的任務。然後,較小的子任務會被分叉,並且它們的結果會被組合在一起以產生最終結果。

方法 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

方法 2 中程式碼的解釋

在這種方法中,我們使用 Executors 類建立一個 ExecutorService,該類提供了一個具有兩個執行緒的固定執行緒池。我們定義了一個 MyTask 類,它實現了 Callable介面,表示要執行的任務。call() 方法執行計算並返回結果。我們使用 submit() 方法將 MyTask 的兩個例項提交到 ExecutorService,該方法返回一個表示計算結果的 Future 物件。最後,我們從 Future 物件中獲取結果並計算最終結果。

方面

Fork/Join 框架

ExecutorService

語法

class ForkJoinTask<V> extends Object

interface ExecutorService extends Executor

設計目的

任務的遞迴分解

非同步任務執行和執行緒管理

粒度

最適合細粒度任務

適用於細粒度和粗粒度任務

任務依賴性

隱式處理遞迴任務分解

需要顯式提交任務

並行性

利用工作竊取演算法進行負載均衡

管理執行緒池和任務執行

結果收集

結果以分層方式合併

結果透過 Future 物件獲取

任務提交

任務內的遞迴分解

獨立提交任務

控制

對執行緒管理的控制有限

對執行緒管理和執行有更大的控制權

用例

分治演算法、遞迴任務

獨立任務的併發執行

結論

總之,Fork/Join 框架和 ExecutorService 都為 Java 中的併發程式設計提供了強大的機制。Fork/Join 框架專為任務的遞迴分解而設計,特別適用於可以分解成子任務的問題。它允許透過利用多個執行緒併合並其結果來實現高效的並行執行。另一方面,ExecutorService 提供了一種更通用的非同步任務執行方法,提供執行緒管理和對執行環境的控制。它非常適合併發執行獨立任務並在需要時獲取其結果。透過理解這些框架的差異和特性,開發人員可以根據其特定需求選擇最合適的方法,從而建立高效且可擴充套件的併發 Java 程式。

更新於: 2023-07-28

392 次瀏覽

啟動你的 職業生涯

透過完成課程獲得認證

開始學習
廣告