目錄
一、創建線程
1.繼承 Thread 類
2.?實現 Runnable 接口
?3.匿名內部類創建 Thread 子類對象
4.?匿名內部類創建 Runnable 子類對象
5.?lambda 表達式創建 Runnable 子類對象
二、Thread 類及常見方法?
2.1 Thread 的常見構造方法
?2.2 Thread 的幾個常見屬性
2.3 啟動一個線程-start()
2.4 中斷一個線程 ?
2.5 等待一個線程-join()
三. 線程的狀態
若想了解線程的基本概念,請參照另一篇博文 :進程和線程?
一、創建線程
1.繼承 Thread 類
class MyThread extends Thread {@Overridepublic void run() {System.out.println("這里是線程運行的代碼");}
}
?2) 創建 MyThread 類的實例
Thread t = new MyThread();
?3) 調用 start 方法啟動線程
t.start(); // 線程開始運行
2.?實現 Runnable 接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("這里是線程運行的代碼");}
}
?2) 創建 Thread 類實例, 調用 Thread 的構造方法時將 Runnable 對象作為 target 參數.
Thread t = new Thread(new MyRunnable());
//或者Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);
?3) 調用 start 方法
t.start(); // 線程開始運行
對比上面兩種方法:
- 繼承 Thread 類, 直接使用 this 就表示當前線程對象的引用.
- 實現 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread(),當前線程實例對象。
?3.匿名內部類創建 Thread 子類對象
????????????在創建Thread類的對象時使用匿名內部類,并重寫方法。
public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();
}
4.?匿名內部類創建 Runnable 子類對象
public static void main(String[] args) { Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();
}
5.?lambda 表達式創建 Runnable 子類對象
????????該方法是最簡便創建線程的方法,?lambda 表達式本質上是一個函數式接口(一個接口中只有一個抽象方法),自身就是run方法,表示邏輯。? ?
???????? lambda 表達式可以理解為:匿名內部類的簡化,實際上為創建了一個類,實現接口,并重寫方法。
//eg: (函數式接口參數)-> 表達式 / {代碼塊};public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();
}
二、Thread 類及常見方法?
????????Thread 類是 JVM 用來管理線程的一個類,換句話說,每個線程都有一個唯一的 Thread 對象與之關聯。
????????Thread 類的對象就是用來描述一個線程執行流的,JVM 會將這些 Thread 對象組織起來,用于線程調度,線程管理。
?????????????????????????
2.1 Thread 的常見構造方法
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("這是我的名字");
Thread t4 = new Thread(new MyRunnable(), "這是我的名字");
?2.2 Thread 的幾個常見屬性
- getId() ,ID 是線程的唯一標識,不同線程不會重復
- getName(),名稱是各種調試工具用到
- getState(),狀態表示線程當前所處的一個情況,下面我們會進一步說明
- getPriority(),優先級高的線程理論上來說更容易被調度到
- isDaemon()?,關于后臺線程,需要記住一點:JVM會在一個進程的所有非后臺線程結束后,才會結束運行。
- isAlive() ,是否存活,即簡單的理解,為 run 方法是否運行結束了
- isInterrupted() ,線程中斷。
?Thread.currentThread(),返回當前線程實例對象。
public class ThreadDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + ": 我還
活著");Thread.sleep(1 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我即將死去");});System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId());System.out.println(Thread.currentThread().getName() + ": 名稱: " + thread.getName());System.out.println(Thread.currentThread().getName() + ": 狀態: " + thread.getState());System.out.println(Thread.currentThread().getName() + ": 優先級: " + thread.getPriority());System.out.println(Thread.currentThread().getName() + ": 后臺線程: " + thread.isDaemon());System.out.println(Thread.currentThread().getName() + ": 活著: " + thread.isAlive());System.out.println(Thread.currentThread().getName() + ": 被中斷: " + thread.isInterrupted());thread.start();while (thread.isAlive()) {}System.out.println(Thread.currentThread().getName() + ": 狀態: " + thread.getState());}
}
2.3 啟動一個線程-start()
????????? 之前我們已經看到了如何通過覆寫 run 方法創建一個線程對象,但線程對象被創建出來并不意味著線程就開始運行了。
? ? ? ? start()內部是會調用到系統api,在系統內核中創建線程
? ? ? ? run(),單純描述了該線程要執行什么工作。
? ? ? ? 本質區別 在于是否在系統內核中創建了一個線程。
2.4 中斷一個線程 ?
? ? ? ? interrupt中斷,讓一個線程停止運行run方法,
目前常見的有以下兩種方式:
- 通過共享的標記來進行溝通,手動設置標志位。
- 調用 interrupt() 方法來通知,Thread 內部包含了一個 boolean 類型的變量作為線程是否被中斷的標記.使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定義標志位。
thread 收到通知的方式有兩種:
1. 如果線程因為調用 wait/join/sleep 等方法而阻塞掛起,則以 InterruptedException 異常的形式通知,清除中斷標志
- 當出現 InterruptedException 的時候, 要不要結束線程取決于 catch 中代碼的寫法. 可以選擇忽略這個異常, 也可以跳出循環結束線程.
2. 否則,只是內部的一個中斷標志被設置, thread 可以通過
- Thread.interrupted() 判斷當前線程的中斷標志被設置,清除中斷標志
- Thread.currentThread().isInterrupted() 判斷指定線程的中斷標志被設置,不清除中斷標志
這種方式通知收到的更及時,即使線程正在 sleep 也可以馬上收到。
????????如下代碼就是第一種?因為調用 wait/join/sleep 等方法而阻塞掛起,則以 InterruptedException 異常的形式通知,清除中斷標志,所以線程run并沒有結束,可以拋出異常break解決。
package thread;// 線程終止
public class Demo9 {public static void main(String[] args) {Thread t = new Thread(() -> {// Thread 類內部, 有一個現成的標志位, 可以用來判定當前的循環是否要結束.while (!Thread.currentThread().isInterrupted()) {System.out.println("線程工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {// 1. 假裝沒聽見, 循環繼續正常執行.e.printStackTrace();// 2. 加上一個 break, 表示讓線程立即結束.// break;// 3. 做一些其他工作, 完成之后再結束.// 其他工作的代碼放到這里.break;}}});t.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("讓 t 線程終止. ");t.interrupt();}
}
2.5 等待一個線程-join()
????????有時,我們需要等待一個線程完成它的工作后,才能進行自己的下一步工作。這時我們需要一個方法明確等待線程的結束。 ? ? ? ?
???????? 主線程中,調用t.join() => 主線程等待 t 線程先結束。
三. 線程的狀態
????????線程的狀態是一個枚舉類型 Thread.State
public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}
- NEW: 安排了工作, 還未開始行動
- RUNNABLE: 可工作的. 又可以分成正在工作中和即將開始工作.
- BLOCKED: 這幾個都表示排隊等著其他事情
- WAITING: 這幾個都表示排隊等著其他事情
- TIMED_WAITING: 這幾個都表示排隊等著其他事情
- TERMINATED: 工作完成了.
//示例
package thread;public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});// 在調用 start 之前獲取狀態, 此時就是 NEW 狀態System.out.println(t.getState());t.start();for (int i = 0; i < 5; i++) {System.out.println(t.getState());Thread.sleep(1000);}t.join();// 在線程執行結束之后, 獲取線程的狀態, 此時是 TERMINATED 狀態System.out.println(t.getState());}
}
?結語:Thread類及常見方法的相關分享到這里就結束了,希望對大家的學習會有幫助,如果大家有什么問題或者不同的見解,歡迎大家評論區的留言,?感謝支持 !!!