目錄
一、圖片總結:
1.1 補充知識
下圖是線程的的六種狀態
- 初始態
- 運行態(就緒是指等待CPU分配執行)
- 阻塞態
- 等待態
- 超時等待態
- 終止態
下圖取之Java線程狀態
1.2 函數整體總結
二、sleep、yield、join
2.1 sleep:
- Api:
sleep(long millis) //參數爲毫秒
sleep(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
- 作用: sleep就是讓線程睡眠,交出CPU
注意點:但是值得注意的是它不會釋放鎖(即如果該線程持有該對象的鎖,那麼sleep後其他線程也無法訪問該對象。)
2.2 yield
- Api:
yield()
- 作用:
yield
讓當前線程交出CPU,但是不能控制時間
注意點: 不會釋放鎖,但是值得注意的是yield只能讓等於或大於自己優先級的線程有機會獲得CPU執行機會
2.3 join
- 結論:先說結論
- join 是通過wait來實現的。
- 但是join不會釋放鎖,因爲他wait的線程是你執行
thread.join
所在的線程(一般是main
線程)
- Api:
join()
join(long millis) //參數爲毫秒
join(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
- 作用:讓當前線程等待子線程結束之後才能運行
注意點
:join實現的順序如下
1.假設在main線程裏運行了子線程A
2.接着設置 A.join()
3.接着main線程會處於waiting狀態
4.直到子線程完成後通知main線程結束等待
-------------特別注意的一個概念----------------->
1.主線程調用的方法就是主線程獲得鎖,子線程run方法裏的調用纔是子線程獲得鎖。
2.所以join通過wait來等待的是主線程,子線程是不會釋放鎖的
- 例子
lic class JoinTest {
public static int a = 0;
public static void main(String ...args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++)
a++;
}
});
thread.start();
//在main線程裏執行了join,所以main線程獲得了join的鎖
//後面join源碼分析裏執行wait是main線程(不要搞亂了)
thread.join();
println(a);
}
//如果不加 join 正常情況下應該是小於10的
//但是加了 join 後,它會等待thread線程執行結束後再執行主線程
//所以答案等於10
- 源碼解析
public final void join(long millis) throws InterruptedException {
synchronized(lock) {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
//判斷子線程是否執行完畢,如果執行完畢的話進行等待
//這邊等待對象是你執行 thread.join 所在的線程 即main
//這邊只要子線程沒執行完畢就無限循環
while (isAlive()) {
lock.wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
lock.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}
三、interrupt
- Api
thread.interrupt()
通知線程中斷(只是一個信號,真正如何處理還是需要自己判斷)Thread.interrupted()
得到線程是否中斷的狀態並且重置狀態(false)Thread.currentThread().isInterrupted()
得到線程是否中斷的狀態
- 注意
調用interrupt()方法,立刻改變的是中斷狀態,但如果不是在阻塞態,就不會拋出異常;如果在進入阻塞態後,中斷狀態爲已中斷,就會立刻拋出異常(阻塞狀態 run方法無法執行得通過其他方法來判斷)
- 例子
這邊舉一個正在阻塞狀態中的例子,如果正常運行的話Run方法會正常執行只用通過判斷isInterrupted
就可以來
業務重點
其中sleep被阻塞100秒這時候如果提前終止使用isInterrupted
是不行的。我們通過阻塞時執行interrupt()
會拋異常的特性來完成該操作,捕捉到異常後,會提前結束阻塞狀態,所以更改stop的屬性來挑出循環,等待run方法執行完畢。
static boolean stop = true;
public static void main(String... args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (stop) {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
stop = false;
}
}
}
});
thread.start();
thread.interrupt();
}
四、優先級
- Api
setPriority
-
作用:
設置優先級,高優先級被優先執行的機率更大 -
優先級等級(1-10):
- 最低優先級 1:Thread.MIN_PRIORITY
- 最高優先級 10:Thread.MAX_PRIORITY
- 普通優先級 5:Thread.NORM_PRIORITY
如果無設置優先級,默認時父線程的優先級,比如我們平時在主線程創建線程優先級都是5.
五、守護線程
- 補充知識:
守護線程是爲其他線程服務的,若只剩下守護線程,那麼守護線程就會自動中斷。 - Api
setDaemon(true)
- 作用:設置爲守護線程
- 注意點
- thread.setDaemon(true)必須在thread.start()之前設置
- 你不能把正在運行的常規線程設置爲守護線程。
- 在Daemon線程中產生的新線程也是Daemon的
4.守護線程中不能做重要的操作,因爲它隨時可能中斷
- 例子
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread daemonThread = new Thread(() -> {
while (true){
System.out.print("我是守護線程" + "\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//設置爲守護線程
daemonThread.setDaemon(true);
daemonThread.start();
System.out.print("開始運行了" + "\n");
}
});
thread.start();
//正常來說daemonThread會一直循環
//當時當他設置爲守護線程後 thread執行結束後 daemonThread也隨之結束。
六、resume()、suspend()、stop()
resume():重啓線程
suspend():掛起線程
stop():停止線程
這三個方法都可能會出現一些不可預料的問題, 所以被廢棄了。