java併發程式設計系列:java併發程式設計背景知識

小孩子4919發表於2019-04-17

標籤: 「我們都是小青蛙」公眾號文章

作業系統發展回顧

裸機

老早之前的計算機只有一個處理器,而一個處理器在同一時刻只能處理一條指令,換句話說,我們的程式碼需要一行一行的按順序被計算機執行,計算機只能把一個程式完整的執行完,然後再執行第二個程式。所以計算機專業的同學們要排隊去機房做實驗,一個人執行完然他的程式後,第二個人再執行自己的程式,這也就意味著所有計算機資源是被一個程式獨佔的,計算機資源包括處理器、記憶體、硬碟、輸入/輸出裝置啥的。這樣的計算機系統我們稱之為裸機

簡單批處理系統

後來人們發現對於價格高昂的計算機裝置來說,在換人的過程中就浪費了好多時間,時間就是金錢,有這些時間可以多執行好多程式了。所以有人寫了一個程式,把所有同學們需要做實驗的程式都放在這個程式裡排個隊,由這個程式來協調各個同學們的程式執行,一個執行完了立即換成另一個,這樣就不用人工干預了,所以他們把這樣的系統叫做簡單批處理系統,而那個負責協調各個童鞋們程式的程式,就是所謂的作業系統的雛形。

多道批處理系統

我們知道,處理器的速度是嗖嗖的,比記憶體訪問的速度快好多個數量級,而記憶體又比硬碟、印表機等I/O裝置啥的快好多個數量級,而程式執行過程中又免不了從硬碟裡讀個檔案,往印表機輸出個啥的,所以處理器浪費了好多時間等待這些I/O操作的完成。再一次,時間就是金錢,為了儘可能的剝削計算機的運算能力,在程式遇到I/O操作或者什麼其他會阻塞程式執行的操作時,處理器會轉向執行其它的程式,什麼時候這個阻塞的操作完成了,再掉過頭繼續執行它。從巨集觀上看,處理器可以各個程式輪流執行,所以這樣的系統就稱為多道批處理系統

分時處理

有的同學對排隊執行程式這個事兒很有意見,大家都是學生,憑啥先執行你的後執行我的,你要是寫了個while(true),那大家都得玩兒完~所以人們給每個程式都分配一點處理器時間去輪流執行,每個程式分配到的執行時間就叫做時間片,這個過程也叫做分時處理。又因為處理器速度太快了,時間片的大小可以做到微秒毫秒的大小,所以這個切換的過程對於人來說根本感覺不出來,所以看起來像各個程式在同時執行。不過後來人們在一臺計算機上又裝了多個處理器,就是我們常聽說的4核8核啥的,所以也可能真正的在同時執行。

時間片具體設定為多大,處理器怎麼切換各個程式的執行,這些工作就是所謂的作業系統來控制的。

程式

程式的概念和特點

我們自己寫的程式,也就是所謂的使用者程式是由作業系統來管理的,人們把一個執行著的程式叫做一個程式(英文名:Process),每個程式都有這麼兩個特點:

  1. 資源所有權

    程式在執行過程中需要一定的資源,比如記憶體、I/O啥的,這些東西不能在不同程式間共享,假如一個程式佔了另一個程式的記憶體,那另一個程式的資料不就丟失了麼;一個程式正在使用印表機輸出東西,另一個程式也使用的話,不就尷尬了麼。所以程式所擁有的這些資源是不能共享的,而這種資源分配的活是由作業系統來管理的。

  2. 排程/執行

    作業系統會為它管理的程式分配時間片,來排程哪個程式應該被處理器處理,哪個應該先休息一會兒。

所以我們現在電腦裡每個執行著的程式都是一個程式,可以開啟你的工作管理員(windows)或者活動監視器(mac),看到我們的電腦裡其實有好多好多程式喔,什麼QQ、微信、音樂播放器、視訊播放器啥的。

程式的狀態

在作業系統級別上,程式根據它執行的情況,可以分成下邊5種狀態

  1. 新建

    剛剛建立的那個時刻,作業系統會為這個程式在記憶體中建立相應的資料,比如分配這個程式的ID,優先順序什麼的~這個狀態持續的時間比較短。

  2. 就緒

    一旦該程式相關的一些資料建立好了,這個程式就會被放在一個叫就緒佇列的佇列裡,之後作業系統就會從這個佇列裡挑一個程式放到處理器裡執行。

  3. 執行

    這個程式被作業系統分配了時間片,處理器開始執行它。

  4. 阻塞

    在執行程式的過程中,可能遇到某些阻塞的動作,比如I/O操作,處理器如果一直等待該阻塞動作完成的話就太浪費時間了,所以會把等待阻塞動作完成的程式放到一個叫阻塞佇列的佇列裡,之後並不會從這個佇列裡挑選即將執行的程式,而是直到該阻塞動作完成,才重新把該程式放到就緒佇列裡等待執行。

  5. 退出

    該程式執行完畢,或者遇到了什麼錯誤,或者作業系統就是想弄死它,它就被殺死了,這個狀態裡作業系統可能還會記錄一下死因啥的,這個過程也很短。

這些狀態的具體轉換過程看下圖:

image_1c3uvlhbnhvpsd5j91ibi1k1j9.png-98.5kB

序列程式設計和並行程式設計

到目前為止,我們的程式設計模式都是序列程式設計,也就是處理器執行完一條指令再執行另一條。舉個例子啊,比如你媽給你佈置了兩個任務:一是去打瓶醬油,二是去燒一壺水,按照序列程式設計的方式就是兩個任務依次進行,也就是說你先打醬油,然後回來再燒水。這麼做沒啥問題,但是沒有效率啊,所以你也可以先把水燒上,然後去打醬油,回來正好水燒開了~這種多個任務同時進行的程式設計方式就叫做並行程式設計

回到計算機中來,序列程式設計的方式就是我們把所有要完成的任務放到一個程式中去執行,而並行程式設計的方式就是我們去開幾個程式同時執行(注:這個同時可能是假的,如果只有單個處理器,那就是看上去是同時的)。這種並行程式設計的優勢是顯而易見的:

  1. 充分利用多個處理器

    現代計算機的處理器越來越多,不用白不用。如果所有的任務都塞到1個程式中,而計算機實際有100個處理器,那麼將會有99%的計算能力將被浪費。

  2. 防止任務的阻塞

    即使是在單個處理器中,為多個任務建立多個程式也是有好處的。先執行的任務可能會需要執行某些I/O操作而造成阻塞,所以就需要等待I/O操作完成才能繼續執行。但是如果為每個任務建立一個程式之後,一個任務阻塞掉並不會影響別的任務的正常執行。

  3. 簡化程式設計的模型

    把一個大任務拆分成若干個小任務自然是會事情清晰許多(注:也不是絕對啊),也就是所謂的大事化小,小事化了~所以把各個任務分配到不同程式裡去執行在我們程式設計方面也會容易一些(注:不是絕對啊)。

執行緒

執行緒的概念

程式是個好東西,可以給每個任務都分配一個程式以達到併發執行的目的。可是執行了一段時間人們發現還是有一些不好的地方的:

  1. 不同程式之間的資源不能夠共享。

    這個對於為了一個大目標細分成的若干小任務很不友好。比方說對於一個網站來說,針對每一個訪問網站的連線都去建立一個程式,如果我們想累加一下總的訪問連線數就比較麻煩了,因為各個程式不能去修改同一塊記憶體。

  2. 建立、切換、銷燬程式成本太大。

    小貼士: 由於我們的主題是java語言裡的併發操作,至於作業系統底層對於建立、切換、銷燬程式都要做哪些東西不是我們嘮叨的範圍,但是這些操作的開銷真的很大,你知道這一點就好了~

再返回頭來看程式的兩個特點,一是對資源的所有權,二是可以作為作業系統排程和執行的單位,這兩個特點是沒有關係的,也就是說獨立的,現代的好多作業系統已經把這兩個特點給拆開了,可以被排程和執行的單位通常被稱作執行緒或者輕量級程式,而擁有資源所有權的單位通常被稱為程式

小貼士: 由於歷史原因,原先的`程式`既是資源的分配單位,也是排程和執行的單位,所以我們開啟一個程式就相當於開啟一個程式。 提出`執行緒`概念之後,這種`開啟一個程式就相當於開啟一個程式`的叫法也被保留了下來。其實現在開啟一個程式的意思是`開啟一個程式並且開啟若干個於這個程式相關聯的執行緒`。

執行緒的特點

看一下這個執行緒的各種特點

  1. 一個程式至少對應一個執行緒

    作業系統每開始執行一個程式,都會為其分配所需的資源以及建立若干個程式執行流,也就是說會建立一個執行緒以及若干執行緒。

  2. 各個執行緒可以共享同一個程式中的各種資源

    因為執行緒是從屬於某個程式,所以程式中的記憶體、I/O啥的資源這個執行緒都可以訪問到。接著上邊那個例子,我們可以對於每一個連到網站的連線都可以分配一個執行緒,然後在申請一塊兒記憶體表示連線數,每建立一個執行緒都把連線數加1,問題就愉快的解決了。

  3. 建立、切換、銷燬執行緒的成本遠低於原先程式的成本

    因為只是一個排程和執行的單位,本來就是原先程式概念的一部分,所以建立、切換、銷燬執行緒的成本就小多了。

  4. 執行緒通訊比程式通訊的效率高

    原先程式間通訊需要配合各種機制,現線上程間由於可以共享記憶體,所以通訊就easy多了。

    小貼士: 至於程式間需要啥機制我這就不說了,不是我們討論的範圍。

所以執行緒的提出完美的解決了一開始提出的把不同任務分配給不同程式執行的缺陷,現在我們可以把不同的任務分配給不同的執行緒去執行,不僅可以方便通訊交流,而且建立和使用的成本還低~

執行緒的狀態

由於執行緒只是單純的繼承了執行緒中排程和執行的特性,所以原先程式擁有的狀態,現線上程一樣擁有,也就是:建立就緒執行阻塞退出

題外話

本篇文章來自小孩子自己的公眾號「我們都是小青蛙」,歡迎大家訂閱,更多幹貨技術文章,時不時扯扯犢子哈:

java併發程式設計系列:java併發程式設計背景知識

小冊

另外,作者還寫了一本MySQL小冊:《MySQL是怎樣執行的:從根兒上理解MySQL》的連結 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL進階的一些核心概念,比如記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想降低普通程式設計師學習MySQL進階的難度,讓學習曲線更平滑一點~ 有在MySQL進階方面有疑惑的同學可以看一下:

java併發程式設計系列:java併發程式設計背景知識

相關文章