超越執行緒池:Java併發並沒有你想的那麼糟糕

ImportNew - Martin發表於2015-03-25

很多人一直嘮叨著併發中的新概念。然而,許多開發人員還沒有機會把過多的注意力都放在上面。在這篇文章中,我們將帶您瞭解Java 8 streams、 Hadoop、 Apache Spark、 Quasar fibers以及響應式程式設計,讓你迅速入門。尤其是如果你不經常用它們的話。一句話,它並不遙遠,它就在我們身邊。

我們該怎麼做?

談到併發,一種很好的方式來形容當前的問題是來回答幾個小問題以便更好的瞭解它:

它是一個資料處理任務麼?如果是這樣的話,它可以分解為獨立的任務單元麼?

作業系統、虛擬機器和你的程式碼之間的關係是什麼?(本地執行緒 VS 輕量級執行緒)

有多少機器和處理器參與?(單核 VS 多核)

讓我們帶著問題,一起找出每個問題的最佳答案吧。

1、從執行緒池到並行流

在Java 8中,我們瞭解到新的流API介面,它允許應用聚集操作,如篩選、排序或者對映資料流。流允許我們做的另一件事情是,在多核機器上應用並行操作。並行流 ——通過把Fork/Join框架引入Java 7將執行緒間的工作分離。Java 6併發庫,我們看到了ExecutorService建立和處理我們的工作執行緒池,這不得不說是個進步。

Fork/Join也建立在ExecutorService之上,與傳統的執行緒主要的區別在於如何線上程和支援多核的機器間分配工作。用一個簡單的 ExecutorService你能完全控制工作執行緒之間的負載分佈,確立每個任務的大小以便執行緒來處理。而Fork/Join,恰好有個work-stealing演算法分配執行緒間的負載。簡而言之,這允許大型任務可以被分成更小單元,並在不同的執行緒間處理,最終我們可以知道——它是為了平衡執行緒間的 工作。然而,這並不是萬能的。

有時並行流會減慢你速度的,所以你需要多想想。在你的方法中使用parallelStream會導致瓶頸和減速(在我們基準測試中跑慢了約15%左右)。假設我們已經執行多個執行緒,在其中一些我們使用parallelStream,線上程池中新增越來越多的執行緒。這可以很容易超過我們的核心處理能力,由於增加了上下文轉換一切都慢下來了。

小結:在單機上並行流使執行緒處理抽象化,在一定程度上這會均衡核心間的負載。然而,如果你想高效使用它們,記住硬體是關鍵而不是生產更多的執行緒而超出機器的處理能力。

2、Apache Hadoop和Apache Spark

接下來談多核機器、 PB級資料和任務,這跟所有從twitter提到的Java或過載機器學習演算法類似。談到Hadoop,不得不說這個應用廣泛的框架及它的組 件:Hadoop分散式檔案系統(HDFS)、資源管理平臺(YARN)、資料處理模組(MapReduce)和其他所需的類庫和工具(Common)。 在這些元件上層還有一些其他很受歡迎的可選工具,比如執行在HDFS上的資料庫(HBase)、查詢語言平臺(Pig)和資料倉儲基礎結構(Hive)。

Apache Spark 作為一種新資料處理模組,以記憶體效能和快速執行的彈性分散式資料集(RDDs)而出名,不同於不能高效使用記憶體和磁碟的Hadoop MapReduce。Databricks公佈的最新標準顯示當用少於10倍節點的時候,對1PB資料的排序Spark比Hadoop快三倍。

典型的Hadoop用例在於查詢資料,而Spark正以其快速的機器學習演算法越來越出名。但這只是冰山一角,Databricks如是說:“Spark 使應用程式在Hadoop叢集中執行在記憶體中快100倍,當執行在磁碟中時甚至快10倍”。

小結:Spark是在Hadoop生態系統中的後起之秀,有一個常見的誤解是我們現在經常談它一些不合作或競爭的事情,但是我認為我們在這正在看到這個框架的發展。

3、Quasar fibers

我們有機會執行在Hadoop,現在讓我們回到單機。事實上,在java多執行緒應用程式和集中在單執行緒上,讓我們眼光再長遠些。就我們而言,HotSpot JVM執行緒與本地系統執行緒相同,持有一個執行緒並且執行在”虛擬“執行緒中,這在fibers中都包含的。Java沒有原生的fibers支援,但是不要擔 心,Quasar通過Parallel Universe解決了我們的問題。

Quasar 是一個開源的JVM庫。它支援fibers(也稱為輕量級執行緒),並且還充當框架的角色,在後面中我會提到。在這上下文轉換是它本質的名字。當我們核心數 量有限,一旦本地執行緒數量越大我們就會收到越來越多的上下文開銷。一種解決這個問題的方式是fibers,使用單執行緒支援”多執行緒“。這看起來像threadcepiton的一個例項。

Fibers還可以被視為一個從執行緒池的進化,當我們通過應用並行流的時候避開了執行緒過載的危險。他們更容易衡量執行緒和允許令人可觀的並行”輕量“執行緒數量。它們不是為了取代執行緒,而是應該用在那些相對來說經常堵塞的程式碼中,就如同擔任真正非同步執行緒的角色。

小結:並行領域在Java併發性中正提供一種新的思路,雖然還沒有版本釋出,但是值得一試。

4、Actor和響應式程式設計

在響應式的官方言論中,最新的釋義有4原則:響應、有彈性、靈活性和訊息驅動。這基本意味著快速、容錯、可伸縮的和支援非阻塞通訊。

讓我們看看Akka Actor是如果支援它的吧。簡單來講Actor有一個狀態和一個特定的行為,通過交換訊息溝通彼此的郵箱。一個Actor系統作為一個整體應該被每個應 用程式建立,擁有一個層次結構將任務分解成更小的任務以便每個角色最多隻有一個監督的角色。一個角色也可以處理這個任務,通過委託給另一個角色將其進一步 分解或在例項失敗的情況下,將它反饋給它的監督者。無論哪種方式,訊息不應該包括行為或者共享可變的狀態,每個角色都有一個獨立的狀態和行為。

它是一個從大多數開發者在使用的併發模型的思考模式的轉移。儘管它起源於70年代,但是為了適應現代應用程式的要求,直到最近幾年它才復甦。並行領域的Quasar也支援Actor,實現的主要區別在於fibers/輕量級執行緒。

小結:相反的,Actor模型需要管理執行緒池,讓它遠離使用工具包。今天面對這種應用程式處理的問題,尤其在我們可以處理擁有更多核心的高併發系統方面又重新有了關注。

總結

關於使用併發或者並行演算法,我們今天通過介紹4種方法來解決問題來應對你需要的場景。希望這有 助於激起你的興趣,以及在這大談併發話題的現在開拓下你的視野。超越執行緒池,有一種將這委託給語言及它的工具的趨勢——關注新的技術並應用它而不是花費無 數個小時解決競態條件和鎖。

相關文章