Java中Thread類詳解及常用的方法
一、Thread 的常見構(gòu)造方法
| 方法 | 說明 |
|---|---|
| Thread() | 創(chuàng)建線程對象 |
| Thread(Runnable target) | 使用 Runnable 對象創(chuàng)建線程對象 |
| Thread(String name) | 創(chuàng)建線程對象并命名 |
| Thread(Runnable target,String name) | 使用 Runnable 對象創(chuàng)建線程對象并命名 |
關(guān)于前兩種方法,在之前的線程創(chuàng)建介紹中有使用到
線程創(chuàng)建根本上來講有兩種創(chuàng)建方法:
創(chuàng)建一個(gè)繼承自 Thread 類的子類,重寫 Thread 中的 run 方法,調(diào)用 start 方法創(chuàng)建一個(gè)實(shí)現(xiàn) Runnable 接口的類,重寫 Thread 中的 run 方法。創(chuàng)建 Thread 實(shí)例,將自己寫的實(shí)現(xiàn) Runnable 接口的類的實(shí)例設(shè)置進(jìn)去,調(diào)用 start 方法
構(gòu)造方法三和四不過是在前面兩種構(gòu)造方法的基礎(chǔ)上多添加了一個(gè)給線程對象命名的參數(shù),方便程序員進(jìn)行調(diào)試。
代碼(以構(gòu)造方法四為例):
public class func7 {
public static void main(String[] args) {
Thread thread = new Thread(() ->{
while (true) {
System.out.println("This is my Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"myThread");
//此處用 lambda 表達(dá)式代替 Runnable 實(shí)例,更加簡潔,添加了一個(gè)參數(shù)指定thread線程的名字
thread.start();
while (true) {
System.out.println("my main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
程序員可以通過 JDK 自帶的jconsole工具來直觀查看這里創(chuàng)建的線程
步驟一:
運(yùn)行程序后,找到自己的 jdk 路徑 -> bin ->jconsole.exe

步驟二:雙擊該 exe 文件。選擇本地進(jìn)程,這里羅列著 Java 進(jìn)程??梢钥匆娢覀兊某绦?func7 ,點(diǎn)擊它。在菜單欄選擇線程一欄

步驟三:查看線程信息

左側(cè)可以選擇需要被查看的線程,可以看見主線程 main和新創(chuàng)建的線程 myThread ,如果沒有重命名操作的話新創(chuàng)建的線程名就會叫 Thread-0,Thread-1 這樣的名字,不方便查看。
右側(cè)顯示了該線程的被讀取的那一瞬間的狀態(tài),堆棧跟蹤顯示的是代碼具體執(zhí)行的位置
二、Thread 的常見屬性
| 屬性 | 方法 |
|---|---|
| ID | getId() |
| 名稱 | getName() |
| 狀態(tài) | getState() |
| 優(yōu)先級 | getPriority() |
| 是否為后臺線程 | isDaemon() |
| 是否存活 | isAlive() |
| 是否被中斷 | isInterrupted() |
解釋:
- 線程的唯一標(biāo)識就是線程 Id
- 名稱在上面的案例中有所體現(xiàn),為調(diào)試提供了便利
- 狀態(tài)表示線程當(dāng)前所處的情況,上面的案例中,線程因?yàn)檎{(diào)用了 sleep 方法就進(jìn)入了阻塞狀態(tài)
- 優(yōu)先級表示該線程被調(diào)度到的難易度,高的更容易被調(diào)度到
- 判斷是否為后臺線程。如果是后臺線程,那么該后臺線程不會影響 java 進(jìn)程的結(jié)束;如果是非后臺線程,JVM 就會等到所有的非后臺線程執(zhí)行完畢,才會結(jié)束運(yùn)行,因此會影響到總進(jìn)程的結(jié)束
- 是否存活是來判斷線程是否還存在的方法。當(dāng)創(chuàng)建出 Thread 實(shí)例對象時(shí),線程未必就創(chuàng)建了,需要調(diào)用 start 方法,才是真正的創(chuàng)建了線程。當(dāng)線程中的 run 方法執(zhí)行完畢,線程結(jié)束被銷毀了,創(chuàng)建出的實(shí)例對象還沒被銷毀回收。所以說,創(chuàng)建出的實(shí)例對象和線程的生命周期是不完全相同的
在線程的狀態(tài)中,除了NEW 和 TERMINATED 以外的狀態(tài)都是活著的
代碼:
public class func8 {
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0;i < 5;i ++) {
System.out.println("新線程~");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"newThread");
System.out.println("新線程狀態(tài):" +t.getState());
//創(chuàng)建了對象,沒創(chuàng)建線程
t.start();
System.out.println("新線程狀態(tài):" +t.getState());
//創(chuàng)建了線程
System.out.println("新線程Id:"+t.getId());
System.out.println("新線程名稱:"+t.getName());
System.out.println("新線程是否為后臺線程:" + t.isDaemon());
System.out.println("新線程是否被中斷:" + t.isInterrupted());
System.out.println("新線程優(yōu)先級:" + t.getPriority());
System.out.println("主線程名稱:"+Thread.currentThread().getName());
while (t.isAlive()) {} //當(dāng)t線程還存在時(shí),主線程就擱這兒循環(huán)著,直到線程結(jié)束
System.out.println("新線程狀態(tài):" +t.getState());//線程結(jié)束
}
}
結(jié)果:

三、創(chuàng)建線程
創(chuàng)建 Thread 類的對象不意味著線程被創(chuàng)建出,start() 方法才是真正的在操作系統(tǒng)內(nèi)部創(chuàng)建一個(gè)新的線程,通過重寫 run 方法來描述需要執(zhí)行的任務(wù),從而真正實(shí)現(xiàn)了多線程運(yùn)行。
四、中斷線程
方法一:手動設(shè)置標(biāo)志位,作為中斷線程的條件
public class func9 {
private static Boolean flag = false;//手動設(shè)置的標(biāo)志位 flag
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!flag) {
//flag 為真時(shí)停止循環(huán)
System.out.println("myThread");
try {
Thread.sleep(1000);//打印一次,阻塞一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();//創(chuàng)建了線程 t
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
//等3秒后,在主線程中將 flag 的值改成 true,從而使線程t循環(huán)條件不成立
}
}
方法二:使用 Thread 實(shí)例中的標(biāo)志位
public class func10 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
//通過 isInerrupted()判斷標(biāo)志位是否為true,為true說明線程要退出
while (!this.isInterrupted()) {
System.out.println("my Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//System.out.println("完善工作");
//break;
}
}
}
};
t.start();//創(chuàng)建新的線程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
//t線程運(yùn)行3秒后,通過 interrupt() 方法將標(biāo)志位設(shè)置為 true
}
}
結(jié)果:

可以看見當(dāng)3秒后標(biāo)志位設(shè)置為 true,希望 t 線程中斷退出時(shí),結(jié)果只是報(bào)了個(gè) InterruptedException 異常。
事實(shí)上,當(dāng)調(diào)用 interrupt 方法時(shí),如果線程為就緒狀態(tài),就會直接修改線程中的標(biāo)志位;如果是阻塞狀態(tài),就會引起 InterruptedException 異常(因?yàn)檎{(diào)用了 sleep 方法,正阻塞著呢,結(jié)果被強(qiáng)行呼醒了)
但收到中斷線程這個(gè)信號后,出現(xiàn)了異常,只是單純的打印了一下異常,并沒有對出現(xiàn)的異常進(jìn)行反應(yīng),于是 t 線程就繼續(xù)循環(huán),就好像中斷線程這個(gè)信號只是提醒了一下,線程打個(gè)日志就完事,當(dāng)做沒聽見。
事實(shí)上,這樣的機(jī)制是有存在的道理的,如果調(diào)用了 interrupt 方法后,線程說中斷就中斷,是非常不符合常理的,此時(shí)并不知道線程執(zhí)行到什么地方,收到中斷的信號后,怎么說也要進(jìn)行收尾工作,由線程自己決定什么時(shí)候被銷毀
因此我們要在捕捉到 InterruptedException 異常后,進(jìn)行工作完善,然后通過 break 跳出循環(huán),結(jié)束線程
方法二和方法一相比更加好。因?yàn)榉椒ㄒ恢械闹袛鄻?biāo)志被修改后,即使當(dāng)時(shí)正在 sleep ,也會把當(dāng)下的 sleep 的時(shí)間過完,才會進(jìn)行下一輪判斷,才知道線程被中斷。方法二即使是在 sleep,收到中斷信號后,就會馬上被喚醒,中斷信息收到的更加的及時(shí)。
結(jié)果:

五、線程等待
通過 join() 方法來決定線程執(zhí)行順序(主要控制結(jié)束線程的順序)。
public class func11 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0;i < 3;i ++) {
System.out.println("my Thread~~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
//t1.join();
for (int i = 0;i < 3;i ++) {
System.out.println("my main!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
結(jié)果:

在沒有調(diào)用 join 方法時(shí),main 線程和 t1 線程是并發(fā)的,結(jié)果輸出是相間的。調(diào)用 join 方法后,main 線程就會阻塞等待,要等 t1 線程執(zhí)行完畢,才會執(zhí)行 join 方法后的內(nèi)容
不帶參數(shù)的 join 方法,等待結(jié)束的條件就是 t1 線程結(jié)束,沒結(jié)束就會一直死等;該方法也可以帶參數(shù),通過參數(shù)來指定等待時(shí)間
六、獲取線程引用
在線程代碼中,需要獲取當(dāng)前線程對應(yīng)的 Thread 類的實(shí)例化對象,才能進(jìn)行更多的操作
方法一:通過繼承 Thread 類創(chuàng)建的線程,可以在重寫的 run 方法中通過 this 獲取當(dāng)前線程的實(shí)例
在上面的中斷線程中的方法二中就是通過 this.isInterrupted() 來獲取當(dāng)前實(shí)例是否被中斷的信息。
如果將創(chuàng)建線程的方式改成創(chuàng)建 Runnable 實(shí)例的方法,當(dāng)前的run 方法就不是 Thread 類的方法,this 指向的是 Runnable,就沒有辦法獲取 Thread 實(shí)例,更沒有辦法使用其中的方法
方法二:通過 Thread 類的 currentThread() 方法,哪個(gè)線程調(diào)用了該方法,就返回哪個(gè)線程的實(shí)例對象

七、線程休眠
該方法在前面經(jīng)常介紹,那就是 sleep 方法
一旦調(diào)用 sleep 方法,線程就會阻塞等待,等待的時(shí)間取決于指定的參數(shù)
操作系統(tǒng)是以線程為單位進(jìn)行調(diào)度的,每個(gè)線程都對應(yīng)著一個(gè) PCB,并通過雙向鏈表組織這些 PCB
操作系統(tǒng)調(diào)度 PCB 時(shí),就是從就緒隊(duì)列中選出一個(gè) PCB 去 CPU 上執(zhí)行,當(dāng)執(zhí)行著的線程調(diào)用了 sleep 方法,這個(gè) PCB 就會被移動到阻塞隊(duì)列中,等到 sleep 的時(shí)間到了,就會回到就緒隊(duì)列中,準(zhǔn)備好被執(zhí)行
join 方法也會產(chǎn)生阻塞等待,就像線程等待中的例子,main 線程執(zhí)行到 join方法后,就到阻塞隊(duì)列中,等待對應(yīng)的 t1 線程執(zhí)行完畢,才會回到就緒隊(duì)列中,做好被執(zhí)行的準(zhǔn)備
八、線程狀態(tài)
從之前的 Thread 的常見屬性這里的代碼案例可以看出來線程的狀態(tài)不只是就緒和阻塞,即使是阻塞也分成好幾種阻塞類型
//線程的狀態(tài)是一個(gè)枚舉類型 Thread.State
//打印 Java 線程中的所有狀態(tài)
public class func13 {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}結(jié)果:

- NEW:表示 Thread 類的對象創(chuàng)建出,但是線程還沒有被創(chuàng)建,即沒有調(diào)用 start 方法
- RUNNABLE:就緒狀態(tài)
- BLOCKED:等待鎖時(shí)的狀態(tài)(期待下一篇關(guān)于線程安全的博客)
- WAITING:通過 wait 方法觸發(fā)(期待之后的博客)
- TIMED_WAITING:通過 sleep 方法產(chǎn)生
- TERMINATED:線程已經(jīng)執(zhí)行完畢,但 Thread 類的對象還存在,未被銷毀
總結(jié)
到此這篇關(guān)于Java中Thread類詳解及常用的方法的文章就介紹到這了,更多相關(guān)Java Thread類方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JDK1.8中的ConcurrentHashMap使用及場景分析
這篇文章主要介紹了JDK1.8中的ConcurrentHashMap使用及場景分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
SpringMVC中@RequestMapping注解的實(shí)現(xiàn)
RequestMapping是一個(gè)用來處理請求地址映射的注解,本文主要介紹了SpringMVC中@RequestMapping注解的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
SpringBoot中使用@ControllerAdvice注解詳解
這篇文章主要介紹了SpringBoot中使用@ControllerAdvice注解詳解,@ControllerAdvice,是Spring3.2提供的新注解,它是一個(gè)Controller增強(qiáng)器,可對controller中被 @RequestMapping注解的方法加一些邏輯處理,需要的朋友可以參考下2023-10-10

