Java中的多執行緒程式設計(超詳細總結)

feng之鋒發表於2020-11-21

**

Java中的多執行緒程式設計(超詳細總結)

**
一、執行緒與多執行緒的概念

執行緒,即單執行緒,是程式的一條執行線索,執行路徑,是程式使用cpu的最小單位。執行緒本身不能執行,它只能執行在程式中,執行緒是依賴於程式存在的。

多執行緒,從字面上理解,就是從多個單執行緒一起執行多個任務。在Java 程式設計中,已經給多執行緒程式設計提供了內建的支援。多執行緒是多工的一種特別的形式,但多執行緒使用了更小的cpu資源開銷。 多執行緒能滿足程式設計師編寫高效率的程式來達到充分利用 CPU 的目的。

二、執行緒與程式之間的關係

程式: 一個程式包括由作業系統分配的記憶體空間,包含一個或多個執行緒。一個執行緒不能獨立的存在,它必須是程式的一部分。一個程式一直執行,直到所有的非守護執行緒都結束執行後才能結束。一條執行緒指的是程式中一個單一順序的控制流,一個程式中可以併發多個執行緒,每條執行緒並行執行不同的任務。

① 程式是指一個記憶體中執行的應用程式,每個程式都有自己獨立的一塊記憶體空間,即程式空間或(虛空間)。程式不依賴於執行緒而獨立存在,一個程式中可以啟動多個執行緒。比如在Windows系統中,一個執行的exe就是一個程式。

② 執行緒是指程式中的一個執行流程,一個程式中可以執行多個執行緒。比如java.exe程式中可以執行很多執行緒。執行緒總是屬於某個程式,執行緒沒有自己的虛擬地址空間,與程式內的其他執行緒一起共享分配給該程式的所有資源。

“同時”執行是人的感覺,線上程之間實際上輪換執行。(這句話簡明闡述了多執行緒的實現機制)

③ 程式在執行過程中擁有獨立的記憶體單元,程式有獨立的地址空間,而多個執行緒共享記憶體,從而極大地提高了程式的執行效率。

④ 執行緒在執行過程中與程式還是有區別的。每個獨立的執行緒有一個程式執行的入口、順序執行序列和程式的出口。但是執行緒不能夠獨立執行,必須依存在應用程式中,由應用程式提供多個執行緒執行控制。

⑤ 程式是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程式是系統進行資源分配和排程的一個獨立單位。

⑥執行緒是程式的一個實體,是CPU排程和分派的基本單位,它是比程式更小的能獨立執行的基本單位。執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程式的其他的執行緒共享程式所擁有的全部資源。

執行緒有自己的堆疊和區域性變數,但執行緒之間沒有單獨的地址空間,一個執行緒包含以下內容:

(1)一個指向當前被執行指令的指令指標;
(2)一個棧;
(3)一個暫存器值的集合,定義了一部分描述正在執行執行緒的處理器狀態的值;
(4)一個私有的資料區。

總而言之:

  1. 一個程式至少有一個程式,一個程式至少有一個執行緒。

  2. 執行緒的劃分尺度小於程式,使得多程式程式的併發性高。

  3. 另外,程式在執行過程中擁有獨立的記憶體單元,而多個執行緒共享記憶體,從而極大地提高了程式的執行效率。

  4. 執行緒在執行過程中與程式還是有區別的。每個獨立的執行緒有一個程式執行的入口、順序執行序列和程式的出口。但是執行緒不能夠獨立執行,必須依存在應用程式中,由應用程式提供多個執行緒執行控制。

  5. 從邏輯角度來看,多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行。但作業系統並沒有將多個執行緒看做多個獨立的應用,來實現程式的排程和管理以及資源分配。這就是程式和執行緒的重要區別。

在Java中,每次程式執行至少啟動2個執行緒: 一個是main執行緒,一個是垃圾收集執行緒。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個JVM實際上就是在作業系統中啟動了一個程式。

三、一個執行緒的生命週期

執行緒是一個動態執行的過程,它也有一個從產生到死亡的過程。下圖顯示了一個執行緒完整的生命週期。在這裡插入圖片描述
1)新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。

2)就緒狀態:
當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,要等待JVM裡執行緒排程器的排程。

3)執行狀態:
如果就緒狀態的執行緒獲取 CPU 資源,就可以執行 run(),此時執行緒便處於執行狀態。處於執行狀態的執行緒最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。

4)阻塞狀態:
如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源之後,該執行緒就從執行狀態進入阻塞狀態。在睡眠時間已到或獲得裝置資源後可以重新進入就緒狀態。可以分為三種:

①等待阻塞: 執行狀態中的執行緒執行 wait() 方法,使執行緒進入到等待阻塞狀態。

②同步阻塞: 執行緒在獲取 synchronized 同步鎖失敗(因為同步鎖被其他執行緒佔用)。

③其他阻塞: 通過呼叫執行緒的 sleep() 或 join() 發出了 I/O 請求時,執行緒就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待執行緒終止或超時,或者 I/O 處理完畢,執行緒重新轉入就緒狀態。

5)死亡狀態:
一個執行狀態的執行緒完成任務或者其他終止條件發生時,該執行緒就切換到終止狀態。

四、多執行緒的目的和意義

多執行緒的意義: 其實任何一個程式的執行都需要獲得cpu的執行權,是由cpu來去決定到底是由哪個程式來去執行,那麼多執行緒的存在其實就是“最大限度的利用cpu資源”,當某一個執行緒的處理不需要佔用cpu而之和I/O打交道的時候,讓需要佔用cpu資源的其他執行緒有機會獲得cpu資源。從根本上說,這就是說多執行緒程式設計的目的。

多執行緒的目的: 不同於其他大多數程式語言,Java本身內建了多執行緒的支援。使用多執行緒,可以幫助我們編寫出cpu最大利用率的高效程式,使得空閒時間降到最低,這個對於Java執行的互動式的網路互聯環境是至關重要的,因為空閒時間是公共的。例如,網路的傳輸效率遠遠低於計算機的處理速度,而本地檔案系統資源的讀寫速度也遠遠低於cpu的處理能力。多執行緒使得並且能夠充分利用這些空閒時間。

五、執行緒的實現的方式

①繼承Thread類:

在這裡插入圖片描述
在利用Thread類實現多執行緒的程式碼,需要去重寫的run()方法,但是重寫完run()方法 之後,在main函式中只會利用執行緒類建立物件,然後呼叫的是start()方法

在這裡大家可能會問了,為什麼不是直接呼叫重寫好的run()方法 呢?而是是利用start()方法 來開啟執行緒的操作呢?

這裡我簡單來回答一下吧, 首先通過物件.run() 方法可以執行方法,但是不是使用的多執行緒的方式,就是一個普通的方法,要想實現多執行緒的方式,一定需要通過物件.start() 方法。Java 中實現真正的多執行緒是 start 中的 start() 方法,run() 方法只是一個普通的方法。

②實現Runnable介面:

在這裡插入圖片描述
③使用繼承Thread類的方法和使用實現Runnable介面的方法之間的關係與區別:

(1)Thread類 本身也是實現了Runnable介面,因此也是實現了Runnable介面 中的run方法

(2)當使用繼承Thread類 去實現執行緒時,我們需要重寫run方法,因為Thread類 中的run方法 本身什麼事情都不幹。

(3)當使用實現Runnable介面 去實現執行緒時,我們需要重寫run方法,然後使用new Thread(Runnable) 這種方式來生成執行緒物件,這個時候執行緒物件中的run方法 才會去執行我們自己實現的Runnable介面 中的run方法

相關文章