並發編程之多執行緒(Java)
- 2019 年 10 月 6 日
- 筆記
一、執行緒與進程區別
每個正在系統上運行的程式都是一個進程。每個進程包含一到多個執行緒。執行緒是一組指令的集合,或者是程式的特殊段,它可以在程式里獨立執行。也可以把它理解為程式碼運行的上下文。所以執行緒基本上是輕量級的進程,它負責在單個程式里執行多任務。通常由作業系統負責多個執行緒的調度和執行
使用執行緒可以把佔據時間長的程式中的任務放到後台去處理,程式的運行速度可能加快,在一些等待的任務實現上如用戶輸入、文件讀寫和網路收發數據等,執行緒就比較有用了。在這種情況下可以釋放一些珍貴的資源如記憶體佔用等等。
如果有大量的執行緒,會影響性能,因為作業系統需要在它們之間切換,更多的執行緒需要更多的記憶體空間,執行緒的中止需要考慮其對程式運行的影響。通常塊模型數據是在多個執行緒間共享的,需要防止執行緒死鎖情況的發生。
總結:進程是所有執行緒的集合,每一個執行緒是進程中的一條執行路徑。
二、為什麼要使用多執行緒?
多執行緒提高程式執行效率。
如: 迅雷多執行緒下載、資料庫連接池、分批發送簡訊等。
三、多執行緒創建方式
1、第一種繼承Thread類 重寫run方法
/** * * @classDesc: 功能描述:(創建多執行緒例子-Thread類 重寫run方法) */ class CreateThread extends Thread { // run方法中編寫 多執行緒需要執行的程式碼 public void run() { for (inti = 0; i< 10; i++) { System.out.println("i:" + i); } } } public class ThreadDemo { public static void main(String[] args) { System.out.println("-----多執行緒創建開始-----"); // 1.創建一個執行緒 CreateThread createThread = new CreateThread(); // 2.開始執行執行緒 注意 開啟執行緒不是調用run方法,而是start方法 System.out.println("-----多執行緒創建啟動-----"); createThread.start(); System.out.println("-----多執行緒創建結束-----"); } }
2、第二種實現Runnable介面,重寫run方法
/** * * @classDesc: 功能描述:(創建多執行緒例子-Thread類 重寫run方法) */ class CreateRunnable implements Runnable { @Override publicvoid run() { for (inti = 0; i< 10; i++) { System.out.println("i:" + i); } } } /** * * @classDesc: 功能描述:(實現Runnable介面,重寫run方法) */ public class ThreadDemo2 { public static void main(String[] args) { System.out.println("-----多執行緒創建開始-----"); // 1.創建一個執行緒 CreateRunnable createThread = new CreateRunnable(); // 2.開始執行執行緒 注意 開啟執行緒不是調用run方法,而是start方法 System.out.println("-----多執行緒創建啟動-----"); Thread thread = new Thread(createThread); thread.start(); System.out.println("-----多執行緒創建結束-----"); } }
3、第三種使用匿名內部類方式
public class ThreadDemo3 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { public void run() { for (int i = 0; i< 10; i++) { System.out.println("i:" + i); } } }); thread.start(); } }
四、常用APi
使用繼承Thread類還是使用實現Runnable介面好?
使用實現實現Runnable介面好,原因實現了介面還可以繼續繼承,繼承了類不能再繼承。
獲取執行緒對象以及名稱
常用執行緒api方法
start() 啟動執行緒
currentThread() 獲取當前執行緒對象
getID() 獲取當前執行緒ID Thread-編號 該編號從0開始
getName() 獲取當前執行緒名稱
sleep(long mill) 休眠執行緒
Stop() 停止執行緒,
常用執行緒構造函數
Thread() 分配一個新的 Thread 對象
Thread(String name) 分配一個新的 Thread對象,具有指定的 name正如其名。
Thread(Runable r) 分配一個新的 Thread對象
Thread(Runable r, String name) 分配一個新的 Thread對象
五、守護執行緒
Java中有兩種執行緒,一種是用戶執行緒,另一種是守護執行緒。
用戶執行緒是指用戶自定義創建的執行緒,主執行緒停止,用戶執行緒不會停止
守護執行緒當進程不存在或主執行緒停止,守護執行緒也會被停止。
使用setDaemon(true)方法設置為守護執行緒
thread.setDaemon(true)
# 六、join()方法作用
當在主執行緒當中執行到t1.join()方法時,就認為主執行緒應該把執行權讓給t1
“`java
Thread t1 = new Thread(new Runnable() {
@Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(10); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + "i:" + i); } } }); t1.start(); // 當在主執行緒當中執行到t1.join()方法時,就認為主執行緒應該把執行權讓給t1 t1.join(); for (int i = 0; i < 10; i++) { try { Thread.sleep(10); } catch (Exception e) { } System.out.println("main" + "i:" + i); }
# 七、優先順序 現代作業系統基本採用時分的形式調度運行的執行緒,執行緒分配得到的時間片的多少決定了執行緒使用處理器資源的多少,也對應了執行緒優先順序這個概念。在JAVA執行緒中,通過一個int priority來控制優先順序,範圍為1-10,其中10最高,默認值為5。下面是源碼(基於1.8)中關於priority的一些量和方法。
java
class PrioritytThread implements Runnable {
public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().toString() + "---i:" + i); } }
}
/**
-
@classDesc:
*/
public class ThreadDemo4 {public static void main(String[] args) {
PrioritytThread prioritytThread = new PrioritytThread();
Thread t1 = new Thread(prioritytThread);
Thread t2 = new Thread(prioritytThread);
t1.start();
// 注意設置了優先順序, 不代表每次都一定會被執行。 只是CPU調度會有限分配
t1.setPriority(10);
t2.start();}
}
“`
八、Yield方法
Thread.yield()方法的作用:暫停當前正在執行的執行緒,並執行其他執行緒。(可能沒有效果)
yield()讓當前正在運行的執行緒回到可運行狀態,以允許具有相同優先順序的其他執行緒獲得運行的機會。因此,使用yield()的目的是讓具有相同優先順序的執行緒之間能夠適當的輪換執行。但是,實際中無法保證yield()達到讓步的目的,因為,讓步的執行緒可能被執行緒調度程式再次選中。
結論:大多數情況下,yield()將導致執行緒從運行狀態轉到可運行狀態,但有可能沒有效果。