根據Wikipedia的說法, 并行性是“在多個處理器上同時執行已編程指令和數據的多個實例的某種組合”,并且Java從第1天起就有類和接口來實現此目的(有點……)。您可能將它們稱為: java .lang.Thread , java.lang.Runnable等…并發實用程序( java.util.concurrent包)的作用是簡化并發任務的編碼方式,因此我們的代碼更加簡單和簡潔。 作為開發人員,在具有更高處理資源的計算機上運行應用程序時,我們無需執行任何操作,顯然,我們的應用程序性能會提高,但是我們是否真的在最大限度地利用處理資源? 答案是否定的。
這篇文章將向您展示在處理可分為小問題的問題時,Fork / Join框架將如何最大程度地幫助我們使用處理資源,并且解決這些小問題中的每一個的所有解決方案都將產生大問題的解決方案問題(例如遞歸,分而治之)。
你需要什么
NetBeans 7+或任何其他支持Java 7 JDK 7+的 IDE
圖像模糊 ,來自Oracle的示例
基礎
Fork / Join框架專注于使用計算機中可用的所有處理資源來提高應用程序的性能。 它旨在簡化Divide and Conquer算法中的并行性。 Fork / Join框架背后的魔力在于其工作竊取算法,該算法中的工作線程可以從其他繁忙線程中自由竊取任務,因此所有線程始終都在工作。 以下是開始使用框架時應了解的基礎知識:
- Fork意味著將任務分為子任務并進行處理。
- 聯接意味著將每個子任務的解決方案合并為一個通用解決方案。
- java.lang.Runtime使用此類來獲取可用于Java虛擬機的處理器數量。 為此,請使用方法+ availableProcessors():int 。
- java.util.concurrent.ForkJoinPool框架的主類,是實現工作竊取算法并負責運行任務的類。
- java.util.concurrent.ForkJoinTask抽象類,用于在java.util.concurrent.ForkJoinPool中運行的任務。 將任務理解為整個工作的一部分,例如,如果您需要在數組上做某事,則一個任務可以在位置0到n / 2上工作,而另一個任務可以在位置(n / 2)+1上工作到n-1 ,其中n是數組的長度。
- java.util.concurrent.RecursiveAction抽象任務類的子類,在不需要任務返回結果時使用它,例如,當任務在數組的位置上工作時,它不返回任何內容,因為它在陣列上工作。 為了完成這項工作,您應該實現的方法是compute():void ,請注意void返回值。
- java.util.concurrent.RecursiveTask抽象任務類的子類,當任務返回結果時使用它。 例如,在計算斐波那契數時,每個任務必須返回它計算出的數以加入它們并獲得一般解。 為了完成這項工作,您應該實現的方法是compute():V ,其中V是返回值的類型; 對于Fibonacci示例, V可以是java.lang.Integer。
使用框架時,應定義一個標志,該標志指示是否有必要派生/加入任務或是否應直接計算工作。 例如,在處理數組時,可以指定如果數組的長度大于500_000_000,則應分叉/加入任務,否則,數組足夠小以直接計算。 本質上,接下來應顯示的算法如下:
if(the job is small enough)
{compute directly
}
else
{split the work in two pieces (fork)invoke the pieces and join the results (join)
}
好了,現在理論太多了,我們來看一個例子。
這個例子
模糊圖像需要對圖像的每個像素進行處理。 如果圖像足夠大,我們將需要處理大量像素,因此我們可以使用fork / join對它們進行處理,并最大限度地利用處理資源。 您可以從Java?Tutorials站點下載源代碼。
下載源代碼后,打開NetBeans IDE 7.x并創建一個新項目:

然后從顯示的彈出窗口的Java類別中選擇“具有現有源代碼的Java項目” :

選擇一個名稱和一個項目文件夾,然后單擊下一步>

現在, 在圖像示例中選擇下載了Blur源代碼的文件夾:

并選擇文件ForkBlur.java,然后單擊完成:

將導入源代碼,并創建一個新項目。 請注意,新項目顯示為錯誤,這是因為默認情況下未啟用Java 7:

要解決此問題,請右鍵單擊項目名稱,然后選擇選項屬性 。 在彈出對話框中,轉到“ 庫”,然后從“ Java平臺組合框”中選擇“ JDK 1.7 ”:

現在,轉到選項Sources并從Source / Binary Format ComboBox中選擇JDK 7 :

最后但并非最不重要的一點是,在運行此應用程序時增加分配給虛擬機的內存,因為我們將訪問500萬個位置數組(或更多)。 轉到選項運行并在VM Options TextBox上插入-Xms1024m -Xmx1024m :

單擊確定 ,您的項目應該沒有錯誤進行編譯。 現在,我們需要找到足夠大的圖像,以便可以處理較大的陣列。 一段時間后,由于好奇心機器人的幫助,我發現了火星上的一些很棒的圖像(約150 MB),您可以從此處下載圖像 。 下載圖像后,將其粘貼到項目的文件夾中。
在運行示例之前,我們需要修改源代碼,以便控制何時使用Fork / Join框架運行它。 在ForkBlur.java文件中,轉到第104行,以更改將要使用的圖像的名稱:
//Change for the name of the image you pasted
//on the project's folder.
String filename = 'red-tulips.jpg';
然后,用下面的代碼替換第130至136行:
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);boolean computeDirectly = true;long startTime = System.currentTimeMillis();if (computeDirectly) {fb.computeDirectly();} else {ForkJoinPool pool = new ForkJoinPool();pool.invoke(fb);}long endTime = System.currentTimeMillis();
注意computeDirectly標志。 為true時 ,我們將不使用fork / Join Framework,而是直接計算任務。 如果為false,將使用fork / join框架。
ForkBlur類中的compute():void方法實現了fork / join算法。 它基于數組的長度,當數組的長度大于10_000時,將分派任務,否則將直接計算任務。
在不使用Fork / Join框架( computeDirectly = true )的情況下, 在圖像示例上執行Blur時,您可以看到我的2個處理器,大約花了14 秒鐘完成工作:

您可以看到處理器正在工作,但沒有達到最大。 當使用Fork / Join框架( computeDirectly = false )時,您可以看到它們以100%的速度工作,并且花了將近50%的時間來完成工作:

該視頻顯示了完整的過程:
希望您能看到這個框架有多有用。 當然,您不能在代碼中全部使用它,但是只要您有一個可以分為多個小任務的任務,那么您就知道該呼叫誰。
參考: Java 7:在Java and ME博客上, 與 JCG合作伙伴 Alexis Lopez交流Fork / Join框架 。
翻譯自: https://www.javacodegeeks.com/2012/10/java-7-meet-forkjoin-framework.html