Java多執行緒基礎知識筆記(持續更新)
多執行緒基礎知識筆記
一、執行緒
1.基本概念
- 程式(program):是為完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的程式碼,靜態對象。
- 進程(process):是程式的一次執行過程,或是正在運行的一個程式。是一個動態的過程:有它自身的產生、存在和消亡的過程(生命周期)。
- 進程作為資源分配的單位,系統在運行時會為每個進程分配不同的記憶體區域。
- 執行緒(tread):進程可以進一步細化為執行緒,是一個程式內部的一條執行路徑。
- 若一個進程同一時間並行執行多個執行緒,就是支援多執行緒的。
- 執行緒作為調度和執行的單位,每個執行緒擁有獨立的運行棧和程式計數器(pc),執行緒切換的開銷小。
- 一個進程中的多個執行緒共享相同的記憶體單元/記憶體地址空間 –> 它們從同一堆中分配對象,可以訪問相同的變數和對象。這就使得執行緒間的通訊更簡便、高效。但多個執行緒操作共享的系統資源可能就會帶來安全的隱患。
- 單核CPU與多核CPU的理解:
- 單核CPU,其實是一種假的多執行緒,因為在一個時間單元內,也只能執行一個執行緒的任務。例如:有多車道,但收費站只有一個工作人員在收費,只有交了費才能通過,那麼CPU就好比收費人員,如果某個人不想交錢,那麼收費人員可以把他「掛起「(等他想通了準備好交錢再收費)。但是因為CPU時間單元特別短,因此感覺不出來。
- 多核CPU,才能更好的發揮多執行緒的效率。
- 一個Java應用程式java.exe,其實至少有三個執行緒:main() 主執行緒, gc() 垃圾回收執行緒, 異常處理執行緒。當然如果發生異常,會影響主執行緒。
- 並行與並發
- 並行:多個CPU同時執行多個任務。
- 並發:一個CPU(採用時間片)同時執行多個任務。
2.多執行緒的優點
- 提高應用程式的響應。對圖形化介面更有意義,可增強用戶體驗。
- 提高電腦系統CPU的利用率。
- 改善程式結構。將即長又複雜的進程分為多個執行緒。獨立運行,利於理解和修改。
3.何時需要多執行緒
- 程式需要同時執行兩個或多個任務。
- 程式需要實現一些需要等待的任務時,如用戶輸入、文件讀寫、網路操作、搜索等。
- 需要一些後台運行的程式時。
4.執行緒的創建
方式一:繼承於Thread類
- 創建一個繼承於Thread類的子類
- 重寫Thread類的run方法 –> 將此執行緒執行的操作聲明在run()中
- 創建Thread類的子類的對象
- 通過對象去調用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介面
-
創建一個實現了Runnable介面的類
-
實現類去實現Runnable中的抽象方法:run()
-
創建實現類的對象
-
將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象
-
通過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(); } }
兩種方式的對比
-
開發中優先考慮:實現Runnable介面的方式
原因:
- 實現的方式沒有類的單繼承性的局限性
- 實現的方式更適合來處理多個執行緒有共享數據的情況
-
其實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. 調度
- 調度策略
- 時間片:
- 搶佔式:高優先順序的執行緒搶佔CPU
- 時間片:
- Java的調度方法
- 同優先順序執行緒組成先進先出隊列(先到先服務),使用時間片策略
- 對高優先順序,使用優先調度的搶佔式策略
2. 執行緒的優先順序
-
默認配置:
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
-
設置操作
-
執行緒對象名.getPriority()
獲取執行緒優先順序
-
執行緒對象名.setPriority()
設置執行緒優先順序
-
-
優先順序理解
高優先順序的執行緒要搶佔低優先順序執行緒cpu的執行權。但是只是從概率上講,高優先順序的執行緒高概率的情況下被執行。並不意味著只有當高優先順序的執行緒執行完後,低優先順序的執行緒才執行