這篇文章中不會介紹
sleep
和wait
, 會在其他的文章中介紹
1. stop
停止線程, 不推薦使用.
JDK中已經標識為@Deprecated
, 因為太暴力, 無論線程運行到什么狀態, 都會強制停止, 可能會導致數據的不一致, 并且不會拋出異常.
2. interrupt
中斷線程
2.1 非阻塞狀態的線程
相當于一個中斷的標志位, 調用之后, 只是把這個標志位改成true.
可以通過interrupted()
這個方法來判斷中斷狀態.
也就是說, 單純的調用這個interrupt()
方法, 是不會使得線程的運行中斷的, 如果要中斷線程的運行, 可以通過這樣的代碼來控制
public void run(){
while(true){
if(Thread.currentThread().isInterrupted())
{
System.out.println("Interruted!");
break;
}
Thread.yield(); //這里可以是正常的線程執行操作
}
}
2.2 阻塞狀態的線程
對于可取消的阻塞狀態中的線程, 比如等待在這些函數上的線程, Thread.sleep()
, Object.wait()
, Thread.join()
, 這個線程收到中斷信號后, 會拋出InterruptedException, 同時會把中斷狀態置回為false.
理論上所有會throws InterruptedException的方法都是可以取消阻塞狀態的.
對于取消阻塞狀態中的線程,可以這樣寫代碼
public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
//設置中斷狀態,拋出異常后會清除中斷標記位
Thread.currentThread().interrupt();
}
Thread.yield();//這里可以是正常的線程執行操作
}
}
sleep
的中斷比較容易, 但是wait
方法如果被中斷, 不一定會馬上拋出異常, 如果獲取不到鎖對象, 就會繼續等待, 知道獲取之后才會拋出InterruptedException.
此外, interrupt()
還可以在使用Lock對象的時候, 解決死鎖的問題(可以參考Lock的lockInterruptibly()
方法)
3. suspend和resume
線程掛起(suspend)和繼續執行(resume)
這兩個方法都是Deprecated方法,不推薦使用。
原因在于,suspend
不釋放鎖,因此如果suspend
在synchronized
塊中執行的,并且也沒有其他線程來執行resume
方法, 這個線程將一直占有這把鎖,造成死鎖發生。
4. yield
讓出CPU資源
這個讓出只是一下, 執行完之后, 線程并不是變成等待狀態, 而是從 "運行狀態" 轉換為 "就緒狀態", 也就是說可能這個線程還是可能會馬上搶占到CPU的資源
官方說是可用于debug和test, 基本找不到使用的場景...
5. join
等待其他線程結束
join的本質
while (isAlive()) {
wait(0);
}
詳細的可以查看join的源碼
public static void main(String[] args) throws Exception {
Thread r1 = new Thread(new MyThread());
r1.start();
r1.join();//等待r1線程執行完之后,才會繼續執行下面的語句
System.out.println("主線程結束");
}
上面的代碼, 主線程在執行r1.join()
的時候就會判斷r1.isAlive()
, 如果r1線程還活著, 就wait(0)
既然是wait操作, 肯定會有鎖對象. join方法是synchronized的, 所以調用這個方法的對象就是鎖對象. 上面這個鎖對象就是r1這個線程對象
同樣, 既然是wait, 肯定會有對應的notify來喚醒這個wait
那么問題是誰在哪里調用了notify呢?
在join方法的javadoc中找到了解釋:
Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
意思是在每個線程結束之后, 都有調用this.notifyAll()
, 那么這個操作可以理解為是由JVM自動完成的動作. 上面的代碼則會在r1這個線程結束之后, JVM自動調用this.notifyAll()
, 這里的this相當于r1, 然后把所有等待r1這個鎖對象的線程給喚醒.
所以javadoc中還給了我們一個建議,不要使用wait和notify/notifyAll在線程實例上。因為jvm會自己調用,有可能與你調用期望的結果不同。