超越線程池:在沒有實際環境中Java程序員該如何學習併發?

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

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

超越線程池:在沒有實際環境中Java程序員該如何學習併發?

我們該怎麼做?

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

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

  2. 操作系統、虛擬機和你的代碼之間的關係是什麼?(本地線程 VS 輕量級線程)

  3. 有多少機器和處理器參與?(單核 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種方法來解決問題來應對你需要的場景。希望這有 助於激起你的興趣,以及在這大談併發話題的現在開拓下你的視野。超越線程池,有一種將這委託給語言及它的工具的趨勢——關注新的技術並應用它而不是花費無 數個小時解決競態條件和鎖。

覺得有收穫的朋友可以收藏關注一波。本頭條號內有多個專題,如【數據結構】、【netty專題】、【dubbo專題】、【mysql優化專題】、【redis專題】、【高併發專題】等優質好文。一起學習,共同進步。

相關推薦

推薦中...