天天看點

Java 并發程式設計:程序、線程、并行與并發

一談到Java并發程式設計,我們一般就會聯想起程序、線程、并行、并發等等概念。那麼這些概念都代表什麼呢?程序與線程有什麼關系?并發與并行又是什麼關系呢?

 程序與線程

程序是指程式的一次動态執行過程,通常我們說計算機中正在執行的程式就是程序,每個程式都會對應着一個程序。一個程序包含了從代碼加載到執行完成的一個完整過程,它是作業系統資源配置設定最小單元。

而線程則是比程序更小的執行機關,是CPU排程和分派的基本機關。每個程序至少有一個線程,反過來一個線程隻能屬于一個程序,線程可以對程序所有的資源進行排程和運算。線程既可以由作業系統核心來控制排程,也可以由使用者程式進行控制排程。

Java 并發程式設計:程式、線程、并行與并發

 并發與并行

并發和并行都可以是相對于程序或是線程來說。并發是指一個或若幹個CPU對多個程序或線程之間進行多路複用,用簡單的語言來說就是CPU輪着執行多個任務,每個任務都執行一小段時間,從宏觀上看起來就像是全部任務都在同時執行一樣。并行則是指多個程序或線程同一時刻被執行,這是真正意義上的同時執行,它必須要有多個CPU的支援。如下圖是并發和并行的執行時間圖。對于并發來說,線程一線執行一段時間,然後線程二再執行一段時間,接着線程三再執行一段時間。每個線程都輪流得到CPU的執行時間,這種情況下隻需要一個CPU即能夠實作。對于并行來說,線程一、線程二和線程三是同時執行的,這種情況下需要三個CPU才能實作。



Java 并發程式設計:程式、線程、并行與并發

而對于Java并發,就是在Java平台上實作來實作并發機制,Java平台上提供了線程以及線程并發

 多線程能提高執行效率

前面我們了解到多線程可以實作并發和并行執行,是以多線程能提升總體的效率。如果不支援多線程的話,那麼當某個執行任務進入等待阻塞狀态時,則可能因為阻塞而導緻運作效率低下。如下圖一中,一個請求任務發起請求後則開始等待響應,此時該線程占着CPU又不幹活,從整個運作線上可以看到真正運作(綠色方塊)的時間很少,這就是運作效率低下。但在如果支援多線程并發的情況下,則可以在等待阻塞時去幹其它的活。此外,多CPU環境下,如果一個任務能夠分解成多個小任務,那麼就能夠用多個CPU同時執行它,這樣就能以更加快的速度完成任務,畢竟單個CPU運作能力有限。如下圖二中,一旦将任務分解成三個小任務後,在多CPU環境下則能夠并行執行,大大減少了整體執行時間。

Java 并發程式設計:程式、線程、并行與并發

單線程阻塞狀态

Java 并發程式設計:程式、線程、并行與并發

 多線程能提升使用者體驗

多線程也能提升使用者體驗,如果一個線程的任務既包含耗時的任務又包含使用者互動的任務,那麼則可能會導緻使用者體驗很糟糕。如下圖,假如大家看到這些視窗一直在打轉又無法對其進行操作,是不是很難受?一個線程發起請求後開始等待請求結果,使用者界面則一直卡着沒響應。我們可以通過多線程将任務分為請求任務和界面操作兩部分,這樣就能在請求後保持對界面操作的響應,以便提供更好的使用者體驗。

 多線程讓編碼更難

天下沒有免費的午餐,多線程也是需要付出代價的。從編寫代碼的角度來看,多線程使得編碼變得更加複雜,本質上這是因為多線程機制與現代計算機結構所帶來的。縱使在程式設計語言設計專家的努力下,現在有很多簡化多線程程式設計的語言和模型,但相比于單線程來說多線程的編寫仍然複雜很多。資料從主存儲到CPU中間有若幹層緩存和寄存器,而且多個線程可能通路共享記憶體,這就涉及到資料同步問題,進而增加了多線程程式設計的複雜性。此外,線程與線程之間的通信也比較麻煩,這也增加了多線程編碼的複雜性。總之,盡管很多程式設計語言嘗試為我們提供更便捷的多線程程式設計,但在語言層面仍然無法完全屏蔽掉多線程與計算機結構的複雜性,是以不管我們使用什麼語言都需要為多線程的編碼考慮得更多。

Java 并發程式設計:程式、線程、并行與并發

 上下文切換與資源開銷

多線程除了增加編碼難度外,它還在執行過程中帶來實際的損耗,包括資源開銷和上下文切換開銷。資源開銷主要包括其本身占用的記憶體資源、執行時線程本地棧開銷以及對這些線程進行管理的開銷。而上下文切換開銷則是因為CPU由一個線程切換到另外一個線程是需要做現場保護和現場恢複工作,包括線程辨別、寄存器記憶體、線程狀态、線程優先級、線程資源清單等等。如下圖所示,假設線程一和線程二由某個CPU執行。線程一執行一段時間後将相關資訊儲存到現場資料結構中,而線程資料結構存放在主存儲中,然後從線程二對應的現場資料結構中恢複線程二相關資訊,完成現場恢複後線程二開始執行。接下去的過程反過來,由線程二切換到線程一。

繼續閱讀