天天看點

為什麼需要學習并發程式設計?

并發程式設計

的掌握過程并不容易。我相信為了解決這個問題,你也聽别人總結過并發程式設計的第一原則,那就是不要寫并發程式。這個原則在我剛畢業的那幾年曾經是行得通的,那個時候多核伺服器還是一種奢侈品,系統的并發量也很低,借助資料庫和類似Tomcat這種中間件,我們基本上不用寫并發程式。或者說,并發問題基本上都被中間件和資料庫解決了。

但是最近幾年,并發程式設計已經慢慢成為一項必備技能。

這主要是硬體的驅動以及國内網際網路行業的飛速發展決定的,現在64核的伺服器已經飛入尋常百姓家,大型網際網路廠商的系統并發量輕松過百萬,傳統的中間件和資料庫已經不能為我們遮風擋雨,反而成了瓶頸所在。

于是,并發程式設計最近幾年成為非常熱門的領域,人才稀缺。但與此同時,關于并發程式設計的書籍也漸漸豐富起來了。是以當極客時間團隊和我聊這個專欄的時候,我的第一個疑問就是目前市面上已經有很多這方面的圖書了,而且很多都非常優秀,是否還有必要搞一個這樣的專欄。

但是深入想過之後,我堅定了寫作的信心。這些年接觸的大部分同學,都是工作幾年後很多技術突飛猛進,卻隻有并發程式設計成為瓶頸,雖然并發相關的類庫他們也熟悉,卻總是寫不出正确、高效的并發程式,原因在哪裡?我發現很多人是因為某個地方有了盲點,忽略了一些細節,但恰恰是這些細節決定了程式的正确性和效率。

而這個盲點有時候涉及對作業系統的了解,有時候又涉及一點硬體知識,非常複雜,如果要推薦相關圖書,可能要推薦好幾本,這就有點“大炮打蚊子”的感覺了,效率很差。同時圖書更追求嚴謹性,卻也是以失掉了形象性,是以閱讀的過程也确實有點艱辛。

我想,如果能夠把這些問題解決,那麼做這個事情應該是有意義的。

例如,Java裡synchronized、wait()/notify()相關的知識很瑣碎,看懂難,會用更難。但實際上synchronized、wait()、notify()不過是作業系統領域裡管程模型的一種實作而已,Java SDK并發包裡的條件變量Condition也是管程裡的概念,synchronized、wait()/notify()、條件變量這些知識如果單獨了解,自然是管中窺豹。但是如果站在管程這個理論模型的高度,你就會發現這些知識原來這麼簡單,同時用起來也就得心應手了。

管程作為一種解決并發問題的模型,是繼信号量模型之後的一項重大創新,它與信号量在邏輯上是等價的(可以用管程實作信号量,也可以用信号量實作管程),但是相比之下管程更易用。而且,很多程式設計語言都支援管程,搞懂管程,對學習其他很多語言的并發程式設計有很大幫助。然而,很多人急于學習Java并發程式設計技術,卻忽略了技術背後的理論和模型,而理論和模型卻往往比具體的技術更為重要。

此外,Java經過這些年的發展,Java SDK并發包提供了非常豐富的功能,對于初學者來說可謂是眼花缭亂,好多人覺得無從下手。但是,Java SDK并發包乃是并發大師Doug Lea出品,堪稱經典,它内部一定是有章可循的。那它的章法在哪裡呢?

其實并發程式設計可以總結為三個核心問題:分工、同步、互斥。

所謂分工指的是如何高效地拆解任務并配置設定給線程,而同步指的是線程之間如何協作,互斥則是保證同一時刻隻允許一個線程通路共享資源。Java SDK并發包很大部分内容都是按照這三個次元組織的,例如Fork/Join架構就是一種分工模式,CountDownLatch就是一種典型的同步方式,而可重入鎖則是一種互斥手段。

當把并發程式設計核心的問題搞清楚,再回過頭來看Java SDK并發包,你會感覺豁然開朗,它不過是針對并發問題開發出來的工具而已,此時的SDK并發包可以任你“盤”了。

而且,這三個核心問題是跨語言的,你如果要學習其他語言的并發程式設計類庫,完全可以順着這三個問題按圖索骥。Java SDK并發包其餘的一部分則是并發容器和原子類,這些比較容易了解,屬于輔助工具,其他語言裡基本都能找到對應的。

是以,你說并發程式設計難學嗎?

首先,難是肯定的。因為這其中涉及作業系統、CPU、記憶體等等多方面的知識,如果你缺少某一塊,那了解起來自然困難。其次,難不難學也可能因人而異,就我的經驗來看,很多人在學習并發程式設計的時候,總是喜歡從點出發,希望能從點裡找到規律或者本質,最後卻把自己繞暈了。

我前面說過,并發程式設計并不是Java特有的語言特性,它是一個通用且早已成熟的領域。Java隻是根據自身情況做了實作罷了,當你了解或學習并發程式設計的時候,如果能夠站在較高層面,系統且有體系地思考問題,那就會容易很多。