NO.001- 簡說 Java 並發編程史

這篇文章是Java並發編程思想系列的第一篇,主要從理解Java並發編程歷史的原因和Java並發演進過程兩部分,以極簡地回溯並發編程的歷史,幫助大家從歷史這個角度去了解一門語言一個特性的演進。對歷史理解的越多,思考的越多,未來的方向就會更加堅定。

 

我是誰?從哪來?到哪去?——柏拉圖

 

一、為什麼了解並發編程歷史


沒有一個新事物一出現就是完美的。回溯Java並發演進的歷史,既可以從宏觀的角度了解世界上正在發生的變化[知乎],又可以讓我們真正的理解當時設計的背後邏輯和歷史原因,學習前人決策的智慧,指導我們的工作和生活。然而,Java語言建立在硬體和作業系統之上的,脫離了硬體和作業系統,單單去回溯Java並發歷史,則無法看清楚底層的邏輯。

二、並發的演進


2.1 並發的產生

綜觀電腦歷史,作業系統與電腦硬體的發展息息相關[維基]。電腦的發展經歷了4個階段,電子管電腦(1945-1955)、電晶體電腦(1955-1965)、積體電路電腦(1965-1980)、大規模積體電路電腦(1980-至今)。因此,伴隨著電腦硬體的更新換代,作業系統也經歷了4個階段,分別是手工操作(50年代早期)、單道批處理系統(50年代)、多道批處理系統(60年代初)、分時系統(60年代中)[作業系統發展歷史]。

 

電晶體的發明後,電腦的可靠性提高了一個層級。由於當時機器非常昂貴,人們期望電腦可以長時間運行[MOS]。單道批處理系統支援把一系列的指令預先寫下來,形成一個清單,一次性的交給電腦,這樣電腦就可以連續不斷讀取指令執行相應的操作[海子],通過這種方式提高了電腦的利用率。

 

然而,由於在單道批處理系統中同時只能執行一個任務,任務在輸入輸出時,CPU是空閑;反之在計算時,輸入輸出設備是空閑的。隨著積體電路晶片發明和普及,為了更加使用電腦資源,誕生了軟體兼容的第三代多道批處理系統,支援多個程式同時進入記憶體並交替在CPU中運行,共享系統中的軟硬體資源。解決了上一代系統一個程式運行時,CPU與外設交替空閑和忙碌的問題,再一次提高了CPU和外設的利用率。

[圖摘自:bilibili作業系統的發展和分類]

 

不過人類對電腦的效率、易用性上的追求上是無止境的,主要體現在兩點:

1. 人機交互:對於第三代系統而言,一個程式從提交到運算結果取回往往需要幾個小時,有時候會由於一個小錯誤導致編譯失敗浪費很多時間,因此用戶希望可以獨佔式的使用電腦,用來調試程式,修改錯誤。

2. 提高電腦使用效率:當時電腦還十分昂貴,一台電腦需要同時供多個用戶共享使用,提高電腦的利用率。

 

此時,分時作業系統就應運而生了,分時作業系統引入了時間片的概念,把CPU時間按一定的時間間隔,採用輪轉運行的方式輪流切換給各個終端用戶的程式使用。由於時間間隔很短(linux根據進程的nice值決定如何分配時間片,一般的時間片在ms級別),每個用戶就感覺像獨佔電腦在使用。

[圖摘自:bilibili作業系統的發展和分類]

 

人們為了同時處理多個任務,從第三代多道批處理系統開始,引入了進程。每個進程都對應一塊自己的記憶體空間,不同進程之間互不干擾,同時進程可以保存程式每個時刻的狀態,這樣就為進程切換提供的可能。從微觀角度看同一時刻只有一個進程在使用CPU資源(單核CPU);從宏觀角度看有多個任務在同時在執行,這就讓並發成為可能[海子]。

2.2 執行緒的產生


進程提高了CPU的利用率,但是由於一個進程在一個時間段內只能做一件事情,所以存在一些明顯的不足:

1. 不支援同一時間進行多個事情:如果想同時干兩件事或多件事,進程就無能為力了

2. 進程會被阻塞,無法及時響應:如果進程在執行過程中阻塞了,如等待輸入,整個進程就會掛起,無法繼續執行。

 

當然有些人表示,可以把多個事情拆分到多個任務。bingo!這就是執行緒最初的思想。不過每個進程都會分配單獨的記憶體空間,這種方式會佔用更多的資源。所以睿智的前人就想到,能夠採用孫悟空的分身術,讓同一個進程下的執行緒共同享有進程佔有的資源和地址空間。簡單的理解:進程屬於在處理器這一層上提供的抽象;執行緒則屬於在進程這個層次上再提供了一層並發的抽象。那麼提個問題:執行緒還可以再細分嗎?

 

2.3 Java並發編程的演進

好啦,鋪墊了這麼多,終於要說到我們今天的主角,Java的並發編程的歷史了。Java並發的演進與電腦系統的演進有著相似性,循著歷史的軌跡,我們可以了解到前人是如何在未知的道路上苦苦探索,通過學習前人深邃的思想,來指導我們的工作。

[圖摘自:Java並發編程通識]

 

Java誕生在1990年Sun公司一個內部項目,當時硬體領域出現了價格低廉的單片式電腦系統,使用它可以大幅度提升消費類電子產品(如電視機頂盒、麵包烤箱、行動電話等)的智慧化程度。Sun公司為了搶佔市場先機,在1991年成立了一個稱為Green的項目小組,James Gosling等人期望使用一種新語言來解決這類程式跨平台運行問題,於是Java的前身Oak(橡樹)語言誕生了。(你看,有很多人抱怨工作挑戰太小,無法提升自己,但真正的創新還是要在工作基礎上進行延伸)。下圖是James Gosling,感謝老爺子養活了這麼多java人。

1996年JDK1.0版本發布,Java的目標是write once, run anywhere,由於站在作業系統這個巨人的肩膀上,因此1.0版本就提出了Java語言的記憶體模型,並確定了執行緒模型以及實現,如Thread、Runnable。當時Java在語言層面支援了多執行緒,這是一項非常大膽的創舉,但是好事多磨,從1997年就在Java記憶體模型規範中發現了幾處嚴重的缺陷,這些缺陷造成執行的結果出現混亂,例如:被final修飾的常量值會發生更改,這些缺陷經過了很長一段時間的詬病。

 

每一次技術的革新總離不開硬體的發展,隨著多核架構的出現,雖然Java記憶體模型改造工程難度之大超出了想像,但是Java 的設計者們還是決定重新修訂 Java 的記憶體模型, 經過了長達 3 年的激烈討論,時間線來到了2004年9月,JDK1.5發布,並正式更名為5.0(請記住這一個里程碑式的版本吧)。這個版本正式發布了兩個重大的規範:JSR133和JSR166。JSR-133規範,即Java記憶體模型與執行緒規範。而JSR166的貢獻是引入了java.util.concurrent包,提到concurrent包,我想Doug Lea大神大家一定不會陌生。感謝為Java人提供了這麼易用的並發工具包。

在推出了JDK5.0後,Java反對的聲音越來越少了,但是一個有生命力的語言不會止步於此。隨著大規模數據處理的出現,2003年和2004年,Google公司在國際會議上分別發表了兩篇關於Google分散式文件系統和MapReduce的論文,公布了Google的GFS和MapReduce的基本原理和主要設計思想。2011年,在JDK7中進一步完善了並發流程式控制制功能,引入了fork-join框架。

 

Java從誕生到現在已經有二十年,那麼Java 的未來會怎樣?我想這新的一頁篇章一定有你揮毫書畫的風采。

[圖摘自:部落格園]

三、思想和本質


縱觀,電腦和Java並發的演進歷程,本質是人類壓榨電腦運算能力的歷史,也是人類不斷探索追求極致性能的歷史,而摩爾定律和 Amdahl定律的更替代表了近年來硬體發展從追求處理器頻率到追求多核心並行處理的發展過程[深入理解java虛擬機]。

1. Amdahl 定律通過系統中的並行化與串列化的比重來描述多處理器系統能獲得的運算加速能力。

2. 摩爾定律則用於描述處理器電晶體數量與運行效率之間的發展關係。

 

本文作者: 葛一凡

分享是快樂的,也見證了個人成長曆程,文章大多都是工作經驗總結以及平時學習積累,基於自身認知不足之處在所難免,也請大家指正,共同進步。

 

註:所有非本人內容均以[]標註,踐行原創,踐行知識源頭,從我做起。

參考


  1. Java 多執行緒發展簡史 | 四火的嘮叨

  2. Java並發編程:進程和執行緒之由來 – Matrix海子 – 部落格園

  3. 作業系統發展歷史 – 雲+社區 – 騰訊雲

  4. 2019 王道考研 作業系統_嗶哩嗶哩 (゜-゜)つロ 乾杯~-bilibili

  5. 有了進程為什麼還要執行緒?_tongxinhaonan的專欄-CSDN部落格

  6. Java並發編程之執行緒篇之執行緒的由來(一) | AndyJennifer』Blog

  7. Java並發編程通識 – 羅輯思維技術部落格

  8. Java 並發:並發背景_Rico』s Blogs-CSDN部落格

  9. JSR 133 (Java Memory Model) FAQ

  10. Java 記憶體模型深入分析 – 鏈聞 ChainNews

  11. 鄒恆明. 電腦的心智 作業系統之哲學原理. 機械工業出版社

  12. [荷] Andrew S. Tanenbaum. 現代作業系統[MOS](原書第4版). 機械工業出版社

  13. 論文:The Java Memory Model: a Formal Explanatio