1. 程式人生 > >操作系統與程序運行以及進程簡介 多線程上篇(一)

操作系統與程序運行以及進程簡介 多線程上篇(一)

都是 計數器 該做的事情 恢復 don 文字 陌生 ati 速度

本系列將對Java多線程進行簡單的介紹。 分為上中下三個章節。 上篇對操作系統中關於進程、並發的相關概念以及問題進行了介紹; 中篇對Java多線程的基礎進行介紹; 下篇將會對Java多線程編程提供的工具、模式進行介紹; Java多線程,首先需要了解線程,了解線程又需要對進程有所了解,而了解進程你需要知道程序的概念,知道程序的概念,你還需要了解操作系統。

線程與操作系統

操作系統是對計算機硬件資源的管理程序,是應用程序與計算機硬件交互的中間層,其本質仍舊是運行於硬件電路上的程序 對計算機硬件來說不存在操作系統,只是處理器對指令的執行,不過操作系統是一個特殊一點的程序。
技術分享圖片 而對於應用開發者來說,以JavaWeb為例,我們卻接觸了太多的東西,首先是Java語言本身,然後........... servlet?jsp?MVC?Spring?SpringBoot?ORM?Mybatis?Dubbo? 然而,這些其實仍舊還是Java本身--Java語言編寫的程序,縱然有那麽多的規範,協議,他也只是一個Java編寫的程序 所以不管你用了多少技術,框架,模式,實現了怎麽樣的協議與功能,原理是什麽,也只是人類意識層面上的內容,到底層只有指令。 用到的一些應用軟件,MYSQL?REDIS?也只是程序。 所以,運行於計算機之上的這一切都只是程序 這些程序經過指定的步驟,從高級到低級,從人類可以理解到無法識別,最終轉換為計算機可以識別的指令。
技術分享圖片 我們編寫的所有的源代碼,最終都要轉換成計算機系統可以識別的內容,而計算機系統包括硬件以及運行其上的系統軟件。 我們所有的編碼,都是面向指定的語法,而這門語言本身,則是面向操作系統的,因為外部軟件通常是不能直接操縱硬件資源,需要借助於操作系統。 所以某種程度上可以這樣認為,所有的源代碼都是面向語言的,而語言本身面向操作系統。 技術分享圖片 操作系統提供了對於計算機硬件資源的管理,對於這些資源的訪問,提供了一系列的方法途徑,這些途徑方法如同機器的操作面板,如同駕駛艙的按鈕手柄。 所以說,計算機有什麽不重要,計算機操作系統有什麽才重要。最簡單的例子就是重裝系統後,如果沒有網卡驅動,你的電腦將無法了解Internet,盡管你的網卡就好端端的插在哪裏。
對絕大多數應用程序員來說,操作系統,便是神一樣的存在,所有的一切都要仰仗於他。

什麽是程序?

遵循某種語言的源代碼經過編譯、翻譯等步驟轉換後的一組計算機能識別和執行的指令,這就是程序。 這是一種靜態的資源,當你的電腦中安裝一個軟件後,如果不啟動軟件,該軟件僅僅是占用磁盤空間 一個程序就像一個用漢字(程序設計語言)寫下的紅燒肉菜譜(程序),用於指導懂漢語和烹飪手法的人來做這個菜。菜譜就是存在於紙上的文字。 當程序需要運行時,操作系統會加載該程序的信息到內存中,並且分配CPU時間片以及其他硬件硬件資源,並且會對這些資源進行管理,比如數據加載到內存的什麽位置了? 而且,現代操作系統都可以同時並發執行多個程序,內存中的這些數據又都是哪個程序的?某個軟件在進行切換時執行到哪裏了?等等這些都需要操作系統進行管理 操作系統將程序的一次運行抽象為進程 簡言之,如果 (處理器)按照 菜譜(程序) 做菜(執行程序)這個過程就叫做 下廚做飯(進程) 技術分享圖片 抽象的概念,沒有人會陌生,如果我們想使用Java語言描述一個學生,我們可能會創建一個Student類,裏面有各種屬性,比如姓名、年齡等
public class Student {

private Long id;// id

private String name;// 姓名

private Integer age;// 年齡

private String sex;// 性別

  //.............等等

這樣一個Class就是一個數據結構,通過他對學生進行描述 而進程是操作系統對程序的一次執行的抽象,也就是說一個程序運行需要哪些信息、數據?這些所有的數據項集合就叫做進程。簡言之就是一個程序運行所需信息的描述集合。 我們以類來比喻的話可能是這樣子:
public class 進程 {
private Long 進程號;
private String 程序計數器PC;
private String xxx寄存器;
private String 堆棧內容;
// ............................等等
}
還有一個概念是進程上下文剛才說到現代系統還可以並發的執行多道程序,必然存在著CPU的切換,那麽從一個程序切換到另一個程序時,如何才能夠恢復? 既然進程是程序的一次運行過程中所需要信息的集合,如果在切換時,將這一瞬時狀態,這一集合體各項數據記錄下來,當再次切換回來時,只需要將數據恢復不就好了嗎 進程執行活動全過程的這一個靜態描述叫做進程上下文 進程間的切換,也被稱之為上下文切換。 通俗比喻: 如果只有一個廚房,你做菜做一半了,然後需要讓出來廚房讓別人做,你需要做什麽? 收拾好你的食材,記住你剛才食材放置的位置以及處理的進度,哪個菜洗過了?鹽放過了麽?。。。等等這些數據就是進程上下文,當別人撤出去之後,你需要將這些狀態還原,這就是上下文切換。 隨著現代計算機技術的發展,進程的弊端開始出現,由於進程是資源擁有者,創建、撤消與切換存在較大的時空開銷,因此需要引入輕型進程,線程就是輕量級的進程。 進程仍然是資源分配的基本單位,線程是程序執行的最小單位 線程的出現可以理解為計算機操作系統對於程序的執行進行了更加精細化的控制,將資源分配,程序運行進行了更加細致的分工。 每個線程都運行在進程的上下文中,共享同樣的代碼和全局數據,很顯然,多線程比多進程更容易共享數據。 總之,線程的出現是操作系統技術的發展,為了更加細化分工,節省開銷的一種做法,是在進程的基礎上發展而來的。

並發與並行

下面這幅圖可以很好地解釋並發與並行 技術分享圖片 一個咖啡機兩個隊伍,就是並發;兩個咖啡機,兩個隊伍,就是並行。 並發 concurrent通過CPU調度算法,進行進程間的切換,也就是多任務執行,操作系統將CPU時間片分配給每個進程,給人並行處理的感覺 並行 parallel並行就是同時執行的意思,多個CPU或者多個機器同時執行一段處理邏輯,是真正的同時。

多線程

很久很久很久以前,操作系統以串行的方式運行,當正在執行的程序遇到阻塞操作,比如等待IO時,CPU空閑等待,極大地浪費了CPU 所以後來出現了多任務操作系統,可以對程序進行切換,當遇到阻塞操作時,CPU可以去執行另外的程序,提高了CPU的利用率 對於線程也是如此,多線程技術相當於是應用程序內部的“多任務”就好比一個應用程序內部有多個線程,其中一個線程等待IO操作時,可以切換執行其他的線程,完成其他的任務,所以對於多線程編寫的程序,看起來程序能夠更快的完成。 所以剛才說線程是操作系統對於程序運行過程的更加細致的劃分與掌控,對於一個多線程程序,能夠更加充分的利用CPU資源,看起來執行快了,是因為CPU的效率變高了,而不是程序的運行所需時間變少了 對於一個單CPU系統,對於多任務的實現就是並發,操作系統不斷地進行著切換,將時間片分配給不同的程序,以看起來像多個程序是共同運行的。 通過多線程,將一個應用程序本身拆解為多任務,如果像上面說的某個線程等待IO導致阻塞,可以執行其他的線程任務,那麽將會提高CPU的利用率 但是如果是類似1+2+3+4......+N的計算呢?假設計算過程是均等的,這不會出現IO阻塞的情況,每一次的運算都是相同的,CPU本身也沒有空閑等待的浪費,所以CPU利用率沒有上升,相反還會有線程切換維護的開銷,所以整體看性能或許略有下降。 所以說,單核場景下,盡管多線程在有些場景下可以提高CPU的利用率,但是對於單CPU系統(單核)系統,在有些場景下,反而會降低整體性能。 因為有的時候你並不能提高利用率;而且有的時候即使提高了利用率,如果提高的那一部分利用率,還不足以抵消做的那些不該做的事情的開銷,整體看並不一定是往好的方向發展。 很顯然,對於單CPU(單核)盡管有些場景多線程可以提高利用率,但是有時也並不能,所以多線程編程並沒有強勢發展。 但是後來,CPU主頻的發展越來越緩慢,對於CPU主頻的升級,摩爾定律開始失效了,因為發展太快,集成電路越來越接近極限了。 既然縱向不能發展,人們總是有辦法的,開始橫向發展,不再追究單核的計算速度,而是研究如何能夠將多個獨立的計算單元整合到一個CPU中,也就是現在說的多核。 隨著技術的發展, 能夠裝載的核心數目越來越多 對於多核CPU,能夠真正的做到在同一瞬時,執行多個線程,是真正的並行。 所以很顯然,這種場景對於真正的並行,不管你的程序任務是什麽樣子的,對於多線程程序,必然能夠提高程序的執行速度。 如果只要一個老師輔導三個學生,你需要合理的安排時間任務,才有可能提高整體的效率;但是如果三個學生對應著三個老師同時在輔導,整體的效率肯定是提高的。 所以隨著多核CPU以及超線程技術的發展,多線程編程就顯得格外重要。 如果單核CPU的性能可以無限制的快速提高,軟件開發者完全不用關心多線程編程,一切交給CPU就好了 但是,目前的情況卻是CPU的性能已經達到瓶頸,硬件在橫向發展,所以如果想要提高CPU的利用率,讓你的程序更快的執行,你將不得不面對多線程編程。 《實戰Java高並發程序設計》中提到:“頂級計算機科學家唐納德·爾文·克努斯(Donald Ervin Knuth ),如此評價這種情況: 在我看來,這種現象(並發)或多或少是由於硬件設計者己經無計可施了導致的,他們將摩爾定律失效的責任推脫給軟件開發者。” 也說明了這個問題----現在為什麽要更加關註多線程技術? 多核場景以及超線程技術的發展下,不是你主動地想要去使用多線程技術,而是現有的硬件體系,想要獲得更好地程序性能,你將不得不使用多線程技術進行編程。 當我處理器還是只能一個一個的來的時候,你們是不是多線程並沒有那麽重要 但是當我可以瞬時同時處理多個線程的時候,如果你還是只有一個線程,你每一時刻也只會有一個線程在執行,但是別人-多線程程序,可能就是多個,所以你的程序的速度與別人相比怎麽樣? 盡管借助於多線程技術,因為有線程切換等系統開銷,所以總共需要CPU做的事情,要大於單線程的時候; 但是CPU多核的並行處理能力以及CPU利用率的提高,將會大大的提高程序的整體效率 所以在多核時代,多線程是必須要考慮的問題。

總結

不管是進程還是線程,都是操作系統對於程序執行的抽象描述,是相關數據:寄存器狀態、堆棧值等所有相關數據的集合。 通過進程的相關信息的維護管理,操作系統保障多道程序可以順利的切換執行; 而對於多線程的應用程序,需要開發者對線程的數據等相關信息進行控制,以保證多線程間可以正確的運行。 多線程共享進程資源,而有些資源是互斥的,並不能允許同時訪問,比如對計數器+1,如果臨界區代碼可以同時訪問,可能兩個人同時過來,每個人同時從1開始執行加1操作,結果卻是2,這顯然是不正確的 多線程編程需要解決的核心就是互斥資源的訪問以及如何高效的利用CPU 保障資源的互斥訪問是為了保證程序的正確性,否則再快的程序也沒有意義;如果編寫的程序非常的不合理,邏輯不清晰,反而可能會帶來性能問題,而不是提高效率。 所以多線程相關的技術的確很復雜,而且非常容易出錯,而且學習成本很高,但是,他終歸是為了提高CPU的利用率的同時並且保障臨界資源的正確訪問。 作為多線程編程人員,如同交警,你需要合理的指揮,提高路口的通行效率,盡最大可能緩解交通堵塞情況,而且需要保證不能在你的指揮下還發生了交通事故或者造成了更大的擁堵; 這是兩個主要方面,就是前面提到的效率和互斥訪問。 另外路口我應該清場出來多大空間用來調度指揮?(鎖粒度)過幾分鐘這個方向的走,過幾分鐘那個方向的走(鎖時間)?我是輪流幾秒鐘切換下?還是哪邊車多讓哪邊多走一會還是怎麽樣(鎖偏向)?這些細節非常復雜繁瑣。 在未來的一段時間內,多線程編程模型是必然的趨勢,也是程序員必須要面對的一件事情,過去的單處理器系統,並發可能是多余的,但是今天,已經成為了勢不可擋的趨勢。 隨著技術的發展,多線程的開發也在從復雜往簡單的方向演化(盡管現在仍舊看起來很復雜),隨後可能會慢慢地出現很多集成、封裝、框架等以讓多線程編程更加簡單 就如同EJB-Spring-SpringBoot的發展,企業級應用的開發過程一直在簡化,但是核心原理卻不斷的被封裝在深處,如果不了解底層,只會招式,永遠也打不出來有力的拳頭,所以建議大家盡可能的深入學習多線程 本系列文章作為自己的學習記錄,從操作系統中關於進程線程並發的相關概念切入,開始介紹Java多線程編程。 原文地址:操作系統與程序運行以及進程簡介 多線程上篇(一)

操作系統與程序運行以及進程簡介 多線程上篇(一)