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
方面 |
Fork/Join 框架 |
ExecutorService |
---|---|---|
語法 |
class ForkJoinTask<V> extends Object |
interface ExecutorService extends Executor |
設計目的 |
任務的遞迴分解 |
非同步任務執行和執行緒管理 |
粒度 |
最適合細粒度任務 |
適用於細粒度和粗粒度任務 |
任務依賴性 |
隱式處理遞迴任務分解 |
需要顯式提交任務 |
並行性 |
利用工作竊取演算法進行負載均衡 |
管理執行緒池和任務執行 |
結果收集 |
結果以分層方式合併 |
結果透過 Future 物件獲取 |
任務提交 |
任務內的遞迴分解 |
獨立提交任務 |
控制 |
對執行緒管理的控制有限 |
對執行緒管理和執行有更大的控制權 |
用例 |
分治演算法、遞迴任務 |
獨立任務的併發執行 |
結論
總之,Fork/Join 框架和 ExecutorService 都為 Java 中的併發程式設計提供了強大的機制。Fork/Join 框架專為任務的遞迴分解而設計,特別適用於可以分解成子任務的問題。它允許透過利用多個執行緒併合並其結果來實現高效的並行執行。另一方面,ExecutorService 提供了一種更通用的非同步任務執行方法,提供執行緒管理和對執行環境的控制。它非常適合併發執行獨立任務並在需要時獲取其結果。透過理解這些框架的差異和特性,開發人員可以根據其特定需求選擇最合適的方法,從而建立高效且可擴充套件的併發 Java 程式。