Java7中的ForkJoin併發框架初探(上)——需求背景和設計原理

最近事情較多,很久沒發文章了。前面關於Java併發的文章中主要介紹了併發的概念、思想、JavaSE5中java.util.concurrent包中的工具類的使用和實現源碼的分析。這篇咱們來簡要了解一下JavaSE7中提供的一個新特性 —— Fork Join 框架java

0. 處理器發展和需求背景算法

回想一下併發開發的初衷,其實能夠說是有兩點,或者說能夠從兩個方面看。後端

  • 對於單核的處理器來講,在進行IO操做等比較費時的操做進行時,若是執行任務的方式是單任務的,那麼CPU將會「空轉」,知道IO操做結束。若是有多任務的調度機制,則在一個任務不須要CPU支持的時候,CPU能夠被調度處理其餘任務。簡單地講,併發能夠提升CPU計算資源的利用率。
  • 對於多核,或者多個計算資源的狀況下,併發能夠在某種程度上達到「並行」,即同時運行,縮短了任務完成的時間,提升了任務完成的效率。

咱們再來看一下處理器計算能力的發展(講併發或者並行基本都要提到),Intel的創始人之一Gordon Moore曾經說過一句話,大概意思是:數據結構

當價格不變時,集成電路上可容納的晶體管數目,約每隔18個月便會增長一倍,性能也將提高一倍。併發

咱們能夠這樣理解,處理器的計算能力在必定意義上和芯片上集成的晶體管數量有關,而這項繼承技術的發展史飛快的。可是,什麼事情都是有一個極限的,提高計算性能僅僅靠增長晶體管數量提升處理器主頻是不現實的,因而多核處理器的概念就出來了。框架

隨着在硬件上多核處理器的發展和普遍使用,軟件開發上的變革也在進行。簡單來想,對於多個不相關的小任務來說,能夠分派到不一樣的處理器核心來進行處理。然而,對於一個比較大的任務,如何可以充分利用多核計算資源就是一個值得考慮的問題。工具

解決這個問題的辦法就是「分而治之」,而Fork Join正式這樣一種思路的產物。性能

1. Fork Join 的設計簡介線程

看過《Introduction to Algorithms》(《算法導論》)的朋友們應該還記得,在講到歸併排序(Merge Sort)和快速排序的時候,有一種很簡單又頗有效率的思路就是「分而治之」,即「分治法」。而Fork Join的思路也是同理,只不過劃分以後的任務更適合分派給不一樣的計算資源,能夠並行的完成任務。設計

ForkJoin的任務分解和合並

ForkJoin的任務分解和合並

當計算分別完成以後,最後再合併回來。

簡單來看,就是一個遞歸的分解和合並,直到任務小到能夠接受的程度。

2. Fork Join 設計要點

Fork Join設計出來就是爲了提升任務完成的效率,圍繞這個目標,有一些要點是設計中須要考慮的,下面就給出一些要點。

  • 線程的管理和線程的單純性。基於如上的設計思路,咱們能夠看到子任務之間的相關性是相對比較簡單的,能夠並行處理。爲了提升效率,並不須要重量級的線程結構和對應的線程維護,線程實現簡單就好,知足需求便可,下降維護成本。
  • 隊列機制,硬件支持必定是比較有限的,那麼分解的任務應該用隊列維護起來,一個好的隊列設計是頗有必要的。
  • 「工做竊取」,也就是設計論文原文中提到的 Work Stealing 。對於負載比較輕的線程,能夠幫助負載較重的執行線程分擔任務。

對於使用Fork Join的開發者來說,須要注意:

  • 可用線程數和硬件支持。線程這東西,也是有開銷的東西,絕對不是越多越好,尤爲在硬件基礎有限的狀況下。
  • 任務分解的粒度。和前者有關係,就是分解的任務,「小」到什麼程度是能夠接受的,不可再分。

3. Fork Join數據結構支持

按照如上設計,分解執行一個大的任務,Fork Join至少須要考慮以下一些數據結構。

  • 輕量級的線程結構。
  • 維護線程的線程池,負責線程的建立,數量維護和任務管理。
  • 維護任務,並支持Work Stealing的雙端隊列。以下圖。

ForkJoin隊列

支持ForkJoin任務維護的雙端隊列Deque

對於子任務的分解,能夠從後端取出分解再放入,而對於WorkStealing則能夠從頭部取出,放入其餘隊列的尾部。

到此,本文僅僅是對Fork Join的大體設計思路作一個描述、勾勒。下一篇文章中會對JDK1.7中給出的實現做出分析。

4. Fork Join其它參考

Doug Lea的文章可參見這裏:DougLea關於ForkJoin設計、實現和性能分析的文章原文

相關文章
相關標籤/搜索