?1.線程的常見構造方法
方法 | 說明 |
Thread() | 創建線程對象 |
Thread(Runnable target) | 使用 Runnable 對象創建線程對象 |
Thread(String name) | 創建線程對象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 對象創建線程對象,并命名 |
【了解】Thread(ThreadGroup group, Runnable target) | 線程可以被用來分組管理,分好的組即為線程組,這 個目前我們了解即可 |
2.線程的幾個屬性和方法
屬性 | 獲取方法 |
ID | getId() |
名稱 | getName() |
狀態 | getState() |
優先級 | getPriority() |
是否后臺線程 | isDaemon() |
是否存活 | isAlive() |
是否被中斷 | isInterrupted() |
這里的后臺線程和前臺線程不同,當所有的前臺線程執行完畢,即使后臺線程還在工作中,也會直接自動退出.
只要前臺線程沒執行完,進程就不會結束,即使main結束了,前臺線程也會繼續執行
設置為后臺進程
setDaemon()設為true就是后臺,不設置就是默認前臺
isAlive()表示內核中的PCB是否存在
這個對象的生命周期和PCB的是不完全一樣的
因為我在創建對象之后這個PCB才存在,在執行完進程結束后之后這個PCB才銷毀,而且java中的用戶級線程不是直接映射到操作系統中的原生線程的
3.創建線程的5個方式
其實線程是操作系統提供的一種機制,并給用戶提供了一些api供使用,Java的Thread類只是對其的進一步封裝.
1.繼承Thread類,重寫run方法
2.重寫runnable接口
3.使用匿名內部類實現Thread
4.使用匿名內部類實現Runnable接口
5.使用Lambda表達式
4.start()和run()方法的區別
說實話這兩個方法是八竿子打不著的,為什么要把他們兩個放進來比較呢?
可能有人認為start方法和run方法執行的是一件事情
其實這個理解是大錯特錯的,這兩個方法完全不一樣,我們舉個例子
假設我們在調用main方法中調用一個run方法,你可能以為這樣和使用start方法是一樣的,其實不然,我們在run方法之前加上一個死循環,讓run方法一直無法執行.而start方法卻可以執行,因為start方法是重新創建了一個新的線程,不是在main線程中國運行的,所以不會受main線程中的死循環的干擾..
5.終止一個線程
package Thread;public class ThreadDemo12 {private static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(!isQuit){System.out.println("我是一個線程,工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("線程工作完畢");});t.start();Thread.sleep(3000);isQuit = true;System.out.println("讓t線程退出");} }
最后結果為
這就好比這個時候我在打游戲,女朋友突然叫我去和她一起開心一下,這個時候就是main線程來中斷我這個t線程,我得配合她她才能中斷我的動作,假設我不配合,那她也沒有辦法.
所以這里的線程運行中想要中斷,其實是需要兩個線程中相互配合的,這里就使用了isQuit變量來實現其中的相互配合.
.
其實這里也有一個方法來實現對線程的中斷,我們來嘗試一下
interrupt()
isInterrupted() 判斷是否被打斷
我們不妨來試試用這個來作為標志位
package Thread;public class ThreadDemo13 {public static void main(String[] args) {Thread t = new Thread(()->{while(!Thread.currentThread().isInterrupted()) {System.out.println("我是一個線程,正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("線程執行完畢");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t線程退出");t.interrupt();} }
結果是
我們發現t線程并沒有真正的執行結束,并且捕獲了一個打斷異常
結論:
sleep中的線程被打斷之后,線程并沒有直接退出,而是繼續一直輸出信息
這是因為線程在睡眠中被打斷會拋出一個異常,并將isInterrupted重新設置為false
解決方案,在收到這個打斷的異常之后,直接break跳出循環
有人會覺得還不如之前的isQuit標志位好,想在遇到這個異常信息的時候將isQuit設置為true,其實是行不通的,可以參考我上一篇文章中的變量捕獲.因為匿名內部類中調用的局部變量只能是final修飾的或是事實final的
6.線程中的join方法
join方法能夠實現線程的等待,假如在main線程中調用t.join,此時main線程就會等待t線程執行結束后再繼續工作,t線程執行的時候,此線程屬于阻塞狀態
下面舉個例子說明一下
package Thread;public class ThreadDemo14 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("馬上到...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("終于到了!!!");});t1.start();t1.join();System.out.println("我等你好久了~~~");}}
執行結果如下
注:start方法一定在join方法之前調用
此時main線程等待了t1線程執行完了才開始執行