P7架構師帶你深刻了解線程的發展歷史

P7架構師帶你深刻了解線程的發展歷史

 

專題簡介

做爲一個合格的Java程序員,必需要對併發編程有一個深層次的瞭解,在不少互聯網企業都會重點考察這一塊。可能不少工做3年以上的Java程序員對於這一領域幾乎沒有太多研究。因此在接下來內容中,我會將併發編程整個領域由淺到深作很是全面的分析。程序員

內容導航

  • 從操做系統的發展瞭解進程、線程模型
  • 線程的優點
  • 線程的生命週期
  • 線程的應用場景

1、瞭解進程、線程模型

每次學習一個新技術,我會先去了解這個技術的背景,這個過程看似浪費時間,其實在後續的學習過程當中,可以促進理解不少問題。因此對於線程這個概念,我會先從操做系統講起。由於操做系統的發展帶來了軟件層面的變革。編程

從多線程的發展來看,能夠操做系統的發展分爲三個歷史階段:性能優化

  • 真空管和穿孔卡片
  • 晶體管和批處理系統
  • 集成電路和多道程序設計

最先的計算機只能解決簡單的數學運算問題,好比正弦、餘弦等。運行方式:程序員首先把程序寫到紙上,而後穿孔成卡票,再把卡片盒帶入到專門的輸入室。輸入室會有專門的操做員將卡片的程序輸入到計算機上。計算機運行完當前的任務之後,把計算結果從打印機上進行輸出,操做員再把打印出來的結果送入到輸出室,程序員就能夠從輸出室取到結果。而後,操做員再繼續從已經送入到輸入室的卡片盒中讀入另外一個任務重複上述的步驟。多線程

操做員在機房裏面來回調度資源,形成計算機存在大量的空閒狀態 。而當時的計算機是很是昂貴的,人們爲了減小這種資源的浪費。就採用了 批處理系統來解決架構

批處理操做系統的運行方式:在輸入室收集所有的做業,而後用一臺比較便宜的計算機把它們讀取到磁帶上。而後把磁帶輸入到計算機,計算機經過讀取磁帶的指令來進行運算,最後把結果輸出磁帶上。批處理操做系統的好處在於,計算機會一直處於運算狀態,合理的利用了計算機資源。(運行流程以下圖所示)併發

 

P7架構師帶你深刻了解線程的發展歷史

 

(注:此圖來源於現代操做系統)異步

批處理操做系統雖然可以解決計算機的空閒問題,可是當某一個做業由於等待磁盤或者其餘I/O操做而暫停,那CPU就只能阻塞直到該I/O完成,對於CPU操做密集型的程序,I/O操做相對較少,所以浪費的時間也不多。可是對於I/O操做較多的場景來講,CPU的資源是屬於嚴重浪費的。分佈式

多道程序設計的出現解決了這個問題,就是把內存分爲幾個部分,每個部分放不一樣的程序。當一個程序須要等待I/O操做完成時。那麼CPU能夠切換執行內存中的另一個程序。若是內存中能夠同時存放足夠多的程序,那CPU的利用率能夠接近100%。微服務

在這個時候,引入了第一個概念- 進程, 進程的本質是一個正在執行的程序,程序運行時系統會建立一個進程,而且給每一個進程分配獨立的內存地址空間保證每一個進程地址不會相互干擾。同時,在CPU對進程作時間片的切換時,保證進程切換過程當中仍然要從進程切換以前運行的位置出開始執行。因此進程一般還會包括程序計數器、堆棧指針。高併發

有了進程之後,可讓操做系統從宏觀層面實現多應用併發。而併發的實現是經過CPU時間片不端切換執行的。對於單核CPU來講,在任意一個時刻只會有一個進程在被CPU調度

有了進程之後,爲何還會出現線程呢?

在一個應用進程中,會存在多個同時執行的任務,若是其中一個任務被阻塞,將會引發不依賴該任務的任務也被阻塞。舉個具體的例子來講,咱們日常用word文檔編輯內容的時候,都會有一個自動保存的功能,這個功能的做用是,當計算機出現故障的狀況下若是用戶未保存文檔,則可以恢復到上一次自動保存的點。假設word的自動保存由於磁盤問題致使寫入較慢,勢必會影響到用戶的文檔編輯功能,直到磁盤寫入完成用戶纔可編輯,這種體驗是不好的。若是咱們把一個進程中的多個任務經過線程的方式進行隔離,那麼按照前面提到的進程演進的理論來講,在單核心CPU架構中能夠經過CPU的時間片切換實現線程的調度充分利用CPU資源以達到最大的性能。

咱們用了比較長的篇幅介紹了進程、線程發展的歷史。總的來講是人們對於計算機的要求愈來愈高;對於計算機自己的資源的利用率也在不斷提升。

2、線程的優點

前面分析了線程的發展歷史,這裏簡單總結一下線程有的優點以下

  • 線程能夠認爲是輕量級的進程,因此線程的建立、銷燬要比進程更快
  • 從性能上考慮,若是進程中存在大量的I/O處理,經過多線程可以加快應用程序的執行速度(經過CPU時間片的快速切換)。
  • 因爲線程是CPU的最小調度單元,因此在多CPU架構中可以實現真正的並行執行。每個CPU能夠調度一個線程

這裏有兩個概念不少人沒有搞明白,就是並行和併發

並行:同時執行多個任務,在多核心CPU架構中,一個CPU核心運行一個線程,那麼4核心CPU,能夠同時執行4個線程

併發:同處理多個任務的能力,一般咱們會經過TPS或者QPS來表示某某系統支持的併發數是多少。

總的來講,並行是併發的子集。也就是說咱們能夠寫一個擁有多線程並行的程序,若是在沒有多核心CPU來執行這些線程,那就不能以並行的方式來運行程序中的多個線程。因此併發程序能夠是並行的,也能夠不是。Erlang之父Joe Armstrong經過一張圖型的方式來解釋併發和並行的區別,圖片以下

 

P7架構師帶你深刻了解線程的發展歷史

 

3、線程的生命週期

線程是存在生命週期的,從線程的建立到銷燬,可能會經歷6種不一樣的狀態,可是在一個時刻線程只能處於其中一種狀態

  • NEW:初始狀態,線程被建立時候的狀態,尚未調用start方法
  • RUNNABLE:運行狀態,運行狀態包含就緒和運行兩種狀態,由於線程啓動之後,並非當即執行,而是須要經過調度去分配CPU時間片
  • BLOCKED:阻塞狀態,當線程去訪問一個加鎖的方法時,若是已經有其餘線程得到鎖,那麼當前線程會處於阻塞狀態
  • WAITING:等待狀態,設置線程進入等待狀態等待其餘線程作一些特定的動做進行觸發
  • TIME_WAITING:超時等待狀態,和WAITING狀態的區別在於超時之後自動返回
  • TERMINATED:終止狀態,線程執行完畢

下圖整理了線程的狀態變動過程及變動的操做,每個具體的操做原理,我會在後續的文章中進行詳細分析。

 

P7架構師帶你深刻了解線程的發展歷史

 

 

這裏有一個問題你們可能搞不明白,BLOCKED和WAITING這兩個阻塞有什麼區別?

  • BLOCKED狀態是指當前線程在等待一個獲取鎖的操做時的狀態。
  • WAITING是經過Object.wait或者Thread.join、LockSupport.park等操做實現的
  • BLOCKED是被動的標記,而WAITING是主動操做
  • 若是說得再深刻一點,處於WAITING狀態的線程,被喚醒之後,須要進入同步隊列去競爭鎖操做,而在同步隊列中,若是已經有其餘線程持有鎖,則線程會處於BLOCKED狀態。因此能夠說BLOCKED狀態是處於WAITING狀態的線程從新喚醒的必經的狀態

4、線程的應用場景

線程的出現,在多核心CPU架構下實現了真正意義上的並行執行。也就是說,一個進程內多個任務能夠經過多線程並行執行來提升程序運行的性能。那線程的使用場景有哪些呢?

  • 執行後臺任務,在不少場景中,可能會有一些定時的批量任務,好比定時發送短信、定時生成批量文件。在這些場景中能夠經過多線程的來執行
  • 異步處理,好比在用戶註冊成功之後給用戶發送優惠券或者短信,能夠經過異步的方式來執行,一方面提高主程序的執行性能;另外一方面能夠解耦核心功能,防止非核心功能對核心功能形成影響
  • 分佈式處理,好比fork/join,將一個任務拆分紅多個子任務分別執行
  • BIO模型中的線程任務分發,也是一種比較常見的使用場景,一個請求對應一個線程

合理的利用多線程,能夠提高程序的吞吐量。同時,還能夠經過增長CPU的核心數來提高程序的性能,這就體現了伸縮性的特色

推薦一個交流學習交流圈子:142019080 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多

相關文章
相關標籤/搜索