在開始學習多線程之前,我們需要先了解進程與線程。
進程與線程
最直觀的表現,就是當我們打開 Mac 的「活動監視器」時,會發現有兩個欄,分別是「進程名稱」與「線程(數量)」。
其中,進程可以簡單地理解為程序的一次運行,比如我們打開了「網易雲音樂」,就會有一個「網易雲音樂」的進程。我們好像並沒有見過叫做「kernel_task」的軟件,這類程序其實是系統的后台進程,它隨操作系統的啟動而啟動,完成操作系統的基本服務功能。另外一類程序也被稱為用戶進程,它是由用戶來啟動,完成用戶所需要的具體的應用功能,比如瀏覽網站,聽音樂,上面說的「網易雲音樂」就是一個用戶進程。
一個進程可以有一個或多個線程,上圖線程一欄的數字就代表對應進程中的線程數量。各個線程共享進程的內存空間、系統資源。早期的操作系統其實只有進程,沒有線程。隨着 CPU 計算能力的顯著提升,為了提高 CPU 的利用率,彌補進程獨傲度過於笨重的問題,進程內部演進出了並發調度的需求,於是發明了線程。
進程是操作系統資源分配的最小單位,線程是 CPU 調度的最小單位。
進程的結構
一個進程,大致可以被分為三個部分:代碼段、數據段、進程控制塊。
代碼段:進程的程序指令在內存中的位置,包含需要執行的指令集合。
數據段:進程的操作數據在內存中的位置。
進程控制塊(PCB):包括描述信息和控制信息。
下面我們來說說,PCB 里面一些重要的概念。
進程 ID:進程的唯一標識,在操作系統中我們很常見到他們,即 PID。
進程名稱:跟人名一樣,進程也有各自的名稱。
進程狀態:分為新建態、終止態,運行態,就緒態,阻塞態。
進程優先級:進程調度的重要依據。進程優先級越好,則越有可能被優先調度。
程序起始地址:程序第一行指令的內存地址,程序就是從這個地址開始執行的。
通信信息:進程間通信時的消息隊列。
內存信息:進程的內存占用情況以及內存管理所用的數據結構。
I/O 設備信息:所用的I/O設備編號及相應的數據結構。
文件句柄:所打開文件的信息。
進程上下文:進程的環境,包括 CPU 寄存器、程序計數器(PC)以及各種棧。在進程讓出 CPU 時,進程的上下文環境就會保存在 PCB 中,供下次恢復運行時使用。
線程的結構
與進程相似,一個線程主要由三個部分組成:線程描述信息、程序計數器(PC)、棧內存。
線程描述信息:即線程的基本信息。
PC:記錄了線程下一條指令的代碼段內存地址。
棧內存:線程中的局部變量存儲在棧內存中,這部分內存為線程獨立擁有的,不會在線程之間共享。
那么,線程都有哪些基本信息?
線程 ID:線程的唯一標識。
線程名稱:方便用戶識別,用戶可以指定線程名稱(不指定則系統自動分配)。
線程優先級:線程調度的優先級,優先級越高,獲得 CPU 的執行機會就越大。
線程狀態:線程的執行狀態,分為新建、就緒、運行、阻塞、結束。
其他:比如,是否是守護線程。
Java 中的進程與線程
眾所周知,Java 程序是運行在 JVM 之上的,每當我們使用 Java 命令啟動一個 Java 程序,就會啟動一個 JVM 進程。JVM 會尋找程序的入口(main 方法),運行 main 方法便有了「主線程」。
除了主線程之外,Java 進程還包含一些「守護線程」。比如 GC 線程,它的作用是垃圾回收。