線程簡(jiǎn)介
線程優(yōu)先級(jí)
在 Java 線程中,通過一個(gè)整形成員變量 priority 來控制優(yōu)先級(jí)妖啥,優(yōu)先級(jí)的范圍從 1~10霉颠,在線程構(gòu)建的時(shí)候可以通過 setPriority(int)方法來修改優(yōu)先級(jí),默認(rèn)優(yōu)先級(jí)是 5荆虱,優(yōu)先級(jí)高的線程比優(yōu)先級(jí)低的線程要分配更多的時(shí)間片蒿偎。設(shè)置線程優(yōu)先級(jí)時(shí)朽们,針對(duì)頻繁阻塞(休眠或者 I/O 操作)的線程需要設(shè)置較高的優(yōu)先級(jí),而偏重計(jì)算(需要較多 CPU 時(shí)間或者偏運(yùn)算)的線程則設(shè)置較低的優(yōu)先級(jí)诉位,確保處理器不會(huì)被獨(dú)占骑脱。
線程優(yōu)先級(jí)不能作為程序正確性的依賴,因?yàn)椴僮飨到y(tǒng)可以完全不同理會(huì) Java 線程對(duì)于優(yōu)先級(jí)的設(shè)定苍糠。
線程的狀態(tài)
狀態(tài) | 說明 |
---|---|
NEW | 初始狀態(tài)叁丧,線程被構(gòu)建,但是還沒有調(diào)用 start 方法 |
RUNNABLE | 運(yùn)行狀態(tài)岳瞭,Java 線程將操作系統(tǒng)中的就緒和運(yùn)行兩種狀態(tài)籠統(tǒng)的稱為運(yùn)行中 |
BLOCKED | 阻塞狀態(tài)拥娄,表示線程阻塞于鎖 |
WAITING | 等待狀態(tài),表示線程進(jìn)入等待狀態(tài)瞳筏,進(jìn)入該狀態(tài)表示當(dāng)前線程需要 |
TIME_WAITING | 超時(shí)等待狀態(tài)稚瘾,該狀態(tài)不同于 WAITING,它可以在指定的時(shí)間自行返回的 |
TEAMINATED | 終止?fàn)顟B(tài)姚炕,表示當(dāng)前線程已經(jīng)執(zhí)行完畢 |
線程在創(chuàng)建之后孟抗,調(diào)用 start 方法開始運(yùn)行。當(dāng)線程執(zhí)行 wait 方法之后钻心,線程進(jìn)入等待狀態(tài)凄硼。進(jìn)入等待狀態(tài)的線程需要依靠其他線程的通知才能返回運(yùn)行狀態(tài),而超時(shí)等待狀態(tài)相當(dāng)于等待狀態(tài)的基礎(chǔ)上增加了超時(shí)限制捷沸,也就是超時(shí)時(shí)間到達(dá)時(shí)將會(huì)返回到運(yùn)行狀態(tài)摊沉。當(dāng)線程調(diào)用同步方法時(shí),在沒有獲取鎖的情況下痒给,線程將會(huì)進(jìn)入阻塞狀態(tài)说墨。線程在執(zhí)行 Runnable 的 run 方法之后將會(huì)進(jìn)入到終止?fàn)顟B(tài)。
注意:Java 將操作系統(tǒng)中的運(yùn)行和就緒兩種狀態(tài)合并為運(yùn)行狀態(tài)苍柏。阻塞狀態(tài)是線程阻塞在進(jìn)入synchronized 關(guān)鍵字修飾的方法或代碼塊時(shí)的狀態(tài)尼斧,但是阻塞在 java.concurrent 包中 Lock 接口的線程狀態(tài)卻是等待狀態(tài),因?yàn)?java.concurrent 包中 Lock 接口對(duì)于阻塞的實(shí)現(xiàn)均使用了 LockSupport 類中的相關(guān)方法试吁。
Daemon 線程
Daemon 線程是一種支持型線程棺棵,因?yàn)樗饕挥米鞒绦蛑泻笈_(tái)調(diào)度以及支持性工作。這意味著熄捍,當(dāng)一個(gè) Java 虛擬機(jī)不存在非 Daemon 線程的時(shí)候烛恤,Java 虛擬機(jī)將會(huì)退出∮嗟ⅲ可以通過調(diào)用 Thread.setDaemon(true) 將線程設(shè)置為 Daemon 線程缚柏。
Daemon 線程被用作完成支持型工作,所以在 Java 虛擬機(jī)退出時(shí) Daemon 線程中的 finally 塊不一定會(huì)執(zhí)行到碟贾。
public class DaemonDemo {
public static void main(String[] args) {
Thread thread=new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("Daemon"); //這一句實(shí)際上并沒有執(zhí)行到
}
});
thread.setDaemon(true);
thread.start();
}
}
因此币喧,在構(gòu)建 Daemon 線程時(shí)轨域,不能依靠 finally 塊中的內(nèi)容來確保執(zhí)行關(guān)閉或者清理資源的邏輯。
線程的中斷
理解中斷
中斷可以理解為線程的一個(gè)標(biāo)識(shí)位屬性杀餐,它表示一個(gè)運(yùn)行中的線程是否被其他線程進(jìn)行了中斷操作疙挺。中斷好比其他線程對(duì)該線程打了招呼,其他線程通過調(diào)用該線程的 interrupt() 方法對(duì)其進(jìn)行中斷操作怜浅。
線程通過檢查自身是否被中斷來進(jìn)行響應(yīng)铐然,線程通過方法 isInterrupted() 來進(jìn)行判斷是否被中斷,也可以調(diào)用靜態(tài)方法 Thread.interrupted() 對(duì)當(dāng)前線程的中斷標(biāo)識(shí)位進(jìn)行復(fù)位恶座。如果該線程已經(jīng)處于終結(jié)狀態(tài)搀暑,即使該線程被中斷過,在調(diào)用對(duì)象的 isInterrupted() 時(shí)依舊會(huì)返回 false跨琳。
從 Java 的 API 中可以看到自点,許多聲明拋出 InterruptedException 的方法( 例如 Thread.sleep(long millis)方法 ),在這些方法拋出該異常之前脉让,Java 虛擬機(jī)會(huì)將該線程的中斷標(biāo)識(shí)位清楚桂敛,然后再拋出異常,此時(shí)調(diào)用 isInterrupted() 將會(huì)返回 false溅潜。
public class InterruptedDemo {
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
while (true) {
try {
Thread.sleep(10);
int a = 0; //讓線程具有實(shí)際的意義
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(() -> {
while (true) {
}
});
threadA.start();
threadB.start();
try {
Thread.sleep(2000); //讓線程 A 和 B 充分運(yùn)行
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.interrupt();
threadB.interrupt();
System.out.println(threadA.isInterrupted());
System.out.println(threadB.isInterrupted());
}
}
/**
* 輸出:
* false
* true
*/
可以看出拋出 InterruptedException 的線程標(biāo)志位被清除了术唬。
安全的中斷線程
可以使用標(biāo)識(shí)位來解決,其實(shí)和 interrupt 標(biāo)識(shí)位是一樣的原理滚澜,不過需要注意 interrupt 可以會(huì)因?yàn)閽伄惓6宄藰?biāo)識(shí)位粗仓。
public class SafelyStopThread {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new WorkRunnable();
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(1000);
((WorkRunnable) runnable).setCancel(true);
Runnable runnable1 = new WorkRunnable();
Thread thread1 = new Thread(runnable1);
thread1.start();
Thread.sleep(1000);
thread1.interrupt();
}
static class WorkRunnable implements Runnable {
private long i;
private volatile boolean isCancel = false;
@Override
public void run() {
while (!isCancel && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println(i);
}
public void setCancel(boolean cancel) {
isCancel = cancel;
}
}
}
線程間通信
下篇講(逃