程序:程序是一個靜態的概念,是完成某個功能的指令集合
一、進程
進程是動態的,一般由程序、數據集合和進程控制塊三部分組成,程序用於描述進程要完成的功能,是控制進程執行的指令集;數據集合是程序在執行時所需要的數據和工作區;進程控制塊(PCB)包含進程的描述信息和控制信息,是進程存在的唯一標示
進程具有的特徵:
動態性:進程是程序的一次執行過程,是臨時的,有生命週期的,是動態產生的,動態消亡的
併發性:任何進程都可以同其他進程一起併發執行
獨立性:進程 是系統進行資源分配的一個獨立單位
結構性:進程由程序、數據和進程控制塊三部分組成
二、線程
線程是程序執行中的一個單一的順序控制流程,是CPU調度的最小單位,一個進程有一個或者多個線程,各個線程之間共享進程的內存空間,一個標準的線程由線程ID、當前指令指針(PC),寄存器和堆棧組成
線程的棧被自動分配到進程的內存空間中
線程和進程的區別:
(1)線程是程序執行的最小單位,而進程是操作系統分配資源的最小單位
(2)一個進程由一個或者多個線程組成,線程是一個進程中代碼的不同執行路線
(3)進程之間相互獨立,但是同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)及一些進程級的資源(打開文件和信號),某進程內的線程在其它進程中不可見
(4)調度和切換:線程上下文切換比進程上下文切換要快的多
Java中線程堆棧是系統某個時刻的線程運行狀態
三、Node.js的單線程
Node.js是單線程的,也就是說當多個用戶發出請求的時候,我並不會爲每個用戶去創建一個線程
單線程使得Node.js不必去頻繁的創建、切換線程,使得運行速度加快
單線程保證了絕對的線程安全,不必擔心同一變量同時被多個線程進行讀寫而造成的程序崩潰
單線程的異步和非阻塞
(1)Node.js如何做到I/O的異步和非阻塞呢?
Node.js在底層訪問I/O的時候還是多線程的,Node.js的fs模塊的源碼,裏面會用到libuv來處理I/O,所以在我們看來Node.js的代碼就是非阻塞和異步形式的
(2)阻塞/非阻塞與異步/同步是兩個不同的概念,同步不代表阻塞,但是阻塞肯定就是同步了
舉個現實生活中的例子,我去食堂打飯,我選擇了A套餐,然後工作人員幫我去配餐,如果我就站在旁邊,等待工作人員給我配餐,這種情況就稱之爲同步;若工作人員幫我配餐的同時,排在我後面的人就開始點餐,這樣整個食堂的點餐服務並沒有因爲我在等待A套餐而停止,這種情況就稱之爲非阻塞。這個例子就簡單說明了同步但非阻塞的情況。
再如果我在等待配餐的時候去買飲料,等聽到叫號再回去拿套餐,此時我的飲料也已經買好,這樣我在等待配餐的同時還執行了買飲料的任務,叫號就等於執行了回調,就是異步非阻塞了。
阻塞的單線程
既然Node.js是單線程異步非阻塞的,是不是我們就可以高枕無憂了呢?
還是拿上面那個買套餐的例子,如果我在買飲料的時候,已經叫我的號讓我去拿套餐,可是我等了好久纔拿到飲料,所以我可能在大廳叫我的餐號之後很久纔拿到A套餐,這也就是單線程的阻塞情況。
在瀏覽器中,js都是以單線程的方式運行的
由如下代碼:
var start = Date.now();//獲取當前時間戳
setTimeout(function () {
console.log(Date.now() - start);
for (var i = 0; i < 1000000000; i++){//執行長循環
}
}, 1000);
setTimeout(function () {
console.log(Date.now() - start);
}, 2000);
最終我們的打印結果是:(結果可能因爲你的機器而不同)
1000
3738
對於我們期望2秒後執行的setTimeout函數其實經過了3738毫秒之後才執行,換而言之,因爲執行了一個很長的for循環,所以我們整個Node.js主線程被阻塞了,如果在我們處理100個用戶請求中,其中第一個有需要這樣大量的計算,那麼其餘99個就都會被延遲執行。
其實雖然Node.js可以處理數以千記的併發,但是一個Node.js進程在某一時刻其實只是在處理一個請求