Java多執行緒基礎知識筆記(持續更新)

多執行緒基礎知識筆記

一、執行緒

1.基本概念

  • 程式(program):是為完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的程式碼,靜態對象。
  • 進程(process):是程式的一次執行過程,或是正在運行的一個程式。是一個動態的過程:有它自身的產生、存在和消亡的過程(生命周期)。
    • 進程作為資源分配的單位,系統在運行時會為每個進程分配不同的記憶體區域。
  • 執行緒(tread):進程可以進一步細化為執行緒,是一個程式內部的一條執行路徑。
    • 若一個進程同一時間並行執行多個執行緒,就是支援多執行緒的。
    • 執行緒作為調度和執行的單位,每個執行緒擁有獨立的運行棧程式計數器(pc),執行緒切換的開銷小。
    • 一個進程中的多個執行緒共享相同的記憶體單元/記憶體地址空間 –> 它們從同一堆中分配對象,可以訪問相同的變數和對象。這就使得執行緒間的通訊更簡便、高效。但多個執行緒操作共享的系統資源可能就會帶來安全的隱患。
  • 單核CPU與多核CPU的理解:
    • 單核CPU,其實是一種假的多執行緒,因為在一個時間單元內,也只能執行一個執行緒的任務。例如:有多車道,但收費站只有一個工作人員在收費,只有交了費才能通過,那麼CPU就好比收費人員,如果某個人不想交錢,那麼收費人員可以把他「掛起「(等他想通了準備好交錢再收費)。但是因為CPU時間單元特別短,因此感覺不出來。
    • 多核CPU,才能更好的發揮多執行緒的效率。
    • 一個Java應用程式java.exe,其實至少有三個執行緒:main() 主執行緒, gc() 垃圾回收執行緒, 異常處理執行緒。當然如果發生異常,會影響主執行緒。
  • 並行與並發
    • 並行:多個CPU同時執行多個任務。
    • 並發:一個CPU(採用時間片)同時執行多個任務。

2.多執行緒的優點

  1. 提高應用程式的響應。對圖形化介面更有意義,可增強用戶體驗。
  2. 提高電腦系統CPU的利用率。
  3. 改善程式結構。將即長又複雜的進程分為多個執行緒。獨立運行,利於理解和修改。

3.何時需要多執行緒

  1. 程式需要同時執行兩個或多個任務。
  2. 程式需要實現一些需要等待的任務時,如用戶輸入、文件讀寫、網路操作、搜索等。
  3. 需要一些後台運行的程式時。

4.執行緒的創建

方式一:繼承於Thread類

  1. 創建一個繼承於Thread類的子類
  2. 重寫Thread類的run方法 –> 將此執行緒執行的操作聲明在run()中
  3. 創建Thread類的子類的對象
  4. 通過對象去調用start方法

例子:

//1.創建一個繼承於Thread類的子類
class MyThread extends Thread {
    //2.重寫Thread類的run方法  -->  將此執行緒執行的操作聲明在run()中
    @Override
    public void run() {
        //該執行緒的功能
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        //3.創建Thread類的子類的對象
        MyThread t1 = new MyThread();
        //4.通過對象去調用start方法
        t1.sart();
    }
}
  • 注意:
    • 不能通過直接調用run()的方式啟動執行緒(實質上只是方法的調用,單執行緒)
    • 不可讓正在啟動的執行緒去執行任何程式碼(報錯IllegalThreadException)。需要重新創建新的執行緒對象。

方式二:實現Runnable介面

  1. 創建一個實現了Runnable介面的類

  2. 實現類去實現Runnable中的抽象方法:run()

  3. 創建實現類的對象

  4. 將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象

  5. 通過Thread類的對象調用start()

    (其實調用的是Runnable類型的target)

    例子:

    //1,創建實現Runnable介面的類
    class Mthread implements Runnable {
        @Override
        public void run() {
            //2.重寫Runnable中的抽象方法run()
        }
    }
    
    public class ThreadTest1 {
        public static void main(String[] args) {
            //3.創建實現類的對象
            MThread mThread = new MThread();
            //4.將該對象作為參數傳給Thread類的構造器中,創建Thread類的對象
            Thread t1 = new Thread(mThread);
            //5,通過Thread類的對象調用start()
            t1.start();
        }
    }
    

兩種方式的對比

  1. 開發中優先考慮:實現Runnable介面的方式

    原因:

    1. 實現的方式沒有類的單繼承性的局限性
    2. 實現的方式更適合來處理多個執行緒有共享數據的情況
  2. 其實Thread類本身就繼承了Runnable介面,並內置重寫了的run()。

二、Thread類的有關方法

  • void start() : 啟動執行緒,並執行對象的run()方法
  • run() : 通常需要重寫Thread類中的此方法,將創建的執行緒要執行的操作聲明在此方法中
  • String getName() : 返回執行緒的名稱
  • void setName(String name) : 設置該執行緒名稱
  • static Thread.currentThread() : 返回當前執行緒。在Thread子類中就是this,通常用於主執行緒和Runnable實現類
  • yield() : 釋放當前cpu的執行權
  • join() : 在執行緒a中調用執行緒b的執行緒join(),此時執行緒a進入阻塞狀態,直到執行緒b完全執行完後,執行緒a才能結束阻塞狀態。
  • stop() : 已過時。強制結束當前執行緒。
  • sleep(long millitime) : 讓當前執行緒「睡眠」值定的millitime毫秒。在指定的millitime毫秒內,當前執行緒時阻塞狀態。
  • isAlive() : 判斷當前執行緒是否存活(返回值為boolean)

三、執行緒的調度

1. 調度

  • 調度策略
    • 時間片:image-20210124101553791
    • 搶佔式:高優先順序的執行緒搶佔CPU
  • Java的調度方法
    • 同優先順序執行緒組成先進先出隊列(先到先服務),使用時間片策略
    • 對高優先順序,使用優先調度的搶佔式策略

2. 執行緒的優先順序

  • 默認配置:

    • MAX_PRIORITY:10
    • MIN_PRIORITY:1
    • NORM_PRIORITY:5
  • 設置操作

    • 執行緒對象名.getPriority()

      獲取執行緒優先順序

    • 執行緒對象名.setPriority()

      設置執行緒優先順序

  • 優先順序理解

    高優先順序的執行緒要搶佔低優先順序執行緒cpu的執行權。但是只是從概率上講,高優先順序的執行緒高概率的情況下被執行。並不意味著只有當高優先順序的執行緒執行完後,低優先順序的執行緒才執行