這個框架的目的主要是更好地利用底層平臺上的多核CPU和多處理器來進行處理,解決問題時通常使用分治算法或map/reduce算法來進行.這個框架的名稱來源於使用時的兩個基本操作fork和join,可以類比於map/reduce中的map和reduce操作.fork操作的作用是把一個大的問題劃分成若干個較小的問題.這個劃分過程一般是遞歸進行的,直到得到可以直接進行計算的粒度合適的子問題.在劃分時,需要恰當地選取子問題的大小.太大的子問題不利於通過並行方式來提高性能,而太小的子問題則會帶來較大的額外開銷.每個子問題在計算完成之後,可以得到關於整個問題的部分解.join操作的作用是把這些部分解收集並組織起來,得到最終的完整解.調用join操作的過程也可能是遞歸進行的,於fork操作相對應.
相對於一般的線程池實現,fork/join框架的優勢體現在對其中包含的任務的處理方式上.在一般的線程池中,如果一個線程正在執行的任務由於某些原因無法繼續運行,那麼該線程會處於等待狀態.而在fork/join框架實現中,如果某個子問題由於等待另外一個子問題的完成而無法繼續運行.那麼處理該子問題的線程會主動尋找其他尚未運行的子問題來執行.這種方式減少了線程的等待時間,提高了性能.爲了fork/join框架能夠高效運行,在每個子問題的實現中應該避免使用synchronized關鍵字或其他方式來進行同步,也不應該使用阻塞式I/O操作或過多地訪問共享變量.在理想情況下,每個子問題的實現中都應該只進行CPU相關的計算,並且只使用每個問題的內部對象.唯一的同步應該只發生在子問題和創建它的父問題之間.
一個fork/join框架執行的任務由ForkJoinTask類表示.ForkJoinTask類實現了Future接口,可以按照Future接口的方式來使用.在ForkJoinTask類中最重要的兩個方法是fork和join,其中fork方法用來以異步方式啓動任務的執行,而join方法則等待任務完成並返回執行結果.在創建自己的任務時,最好不要直接繼承自ForkJoinTask類,而要繼承自ForkJoinTask類的子類RecursiveTask或RecursiveAction類.兩者的區別在於RecursiveTask類表示的任務可以返回結果,而RecursiveAction類不行.
在fork/join框架中,任務的執行由ForkJoinPool類的對象來完成.ForkJoinPool類實現了ExecutorService接口,除了執行ForkJoinTask類的對新外,還可以使用一般的Callable和Runnable接口來表示的任務,在ForkJoinPool類的對新中執行的任務大致可以分成兩類:一類是通過execute,invoke或submit方法直接提交的任務;另外一類是ForkJoinTask類的對新在執行過程中產生的子任務,並通過fork方法來運行.一般的做法是表示整個問題的ForkJoinTask類的對象用第一類形式提交,而在執行過程中產生的子任務並不需要進行處理,ForkJoinPool類的對新會負責子任務的執行.
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* <pre>
* 輕量級任務執行框架fork/join詳解,使用該框架計算文件目錄大小
* </pre>
* User: ketqi
* Date: 2013-01-27 17:40
*/
public class ForkJoinDemo {
private static class CalculateTask extends RecursiveTask<Long> {
final File file;
public CalculateTask(final File theFile) {
file = theFile;
}
@Override
public Long compute() {
long size = 0;
if (file.isFile()) {
size = file.length();
} else {
final File[] children = file.listFiles();
if (children != null) {
List<ForkJoinTask<Long>> taskList = new ArrayList<ForkJoinTask<Long>>();
for (final File child : children) {
if (child.isFile()) {
size += child.length();
} else {
taskList.add(new CalculateTask(child));
}
}
//invokeAll方法循環調用task.fork()執行子任務.
for (final ForkJoinTask<Long> task : invokeAll(taskList)) {
//task.join()方法等待任務完成並返回執行的結果
size += task.join();
}
}
}
return size;
}
}
public static void main(String[] args) {
long start = System.nanoTime();
long total = new ForkJoinPool().invoke(new CalculateTask(new File("E:\\workspace\\java7")));
long end = System.nanoTime();
System.out.println("Total Size: " + total + "B");
System.out.println("Time taken: " + (end - start) / 1.0e9);
}
}