jar 退出run_Java终止线程的三种方式
停止一個線程通常意味著在線程處理任務(wù)完成之前停掉正在做的操作,也就是放棄當(dāng)前的操作。
在 Java 中有以下 3 種方法可以終止正在運行的線程:
使用退出標(biāo)志,使線程正常退出,也就是當(dāng) run() 方法完成后線程中止。
使用 stop() 方法強行終止線程,但是不推薦使用這個方法,該方法已被棄用。
使用 interrupt 方法中斷線程。
1. 使用標(biāo)志位終止線程
在 run() 方法執(zhí)行完畢后,該線程就終止了。但是在某些特殊的情況下,run() 方法會被一直執(zhí)行;比如在服務(wù)端程序中可能會使用 while(true) { ... } 這樣的循環(huán)結(jié)構(gòu)來不斷的接收來自客戶端的請求。此時就可以用修改標(biāo)志位的方式來結(jié)束 run() 方法。
public class ServerThread extends Thread {
//volatile修飾符用來保證其它線程讀取的總是該變量的最新的值
public volatile boolean exit = false;
@Override
public void run() {
ServerSocket serverSocket = new ServerSocket(8080);
while(!exit){
serverSocket.accept(); //阻塞等待客戶端消息
...
}
}
public static void main(String[] args) {
ServerThread t = new ServerThread();
t.start();
...
t.exit = true; //修改標(biāo)志位,退出線程
}
}
2. 使用 stop() 終止線程
通過查看 JDK 的 API,我們會看到 java.lang.Thread 類型提供了一系列的方法如 start()、stop()、resume()、suspend()、destory()等方法來管理線程。但是除了 start() 之外,其它幾個方法都被聲名為已過時(deprecated)。
雖然 stop() 方法確實可以停止一個正在運行的線程,但是這個方法是不安全的,而且該方法已被棄用,最好不要使用它。
JDK 文檔中還引入用一篇文章來解釋了棄用這些方法的原因:《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?》
為什么棄用stop:
調(diào)用 stop() 方法會立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 語句中的,并拋出ThreadDeath異常(通常情況下此異常不需要顯示的捕獲),因此可能會導(dǎo)致一些清理性的工作的得不到完成,如文件,數(shù)據(jù)庫等的關(guān)閉。
調(diào)用 stop() 方法會立即釋放該線程所持有的所有的鎖,導(dǎo)致數(shù)據(jù)得不到同步,出現(xiàn)數(shù)據(jù)不一致的問題。
例如,存在一個對象 u 持有 ID 和 NAME 兩個字段,假如寫入線程在寫對象的過程中,只完成了對 ID 的賦值,但沒來得及為 NAME 賦值,就被 stop() 導(dǎo)致鎖被釋放,那么當(dāng)讀取線程得到鎖之后再去讀取對象 u 的 ID 和 Name 時,就會出現(xiàn)數(shù)據(jù)不一致的問題,如下圖:
3. 使用 interrupt() 中斷線程
現(xiàn)在我們知道了使用 stop() 方式停止線程是非常不安全的方式,那么我們應(yīng)該使用什么方法來停止線程呢?答案就是使用 interrupt() 方法來中斷線程。
需要明確的一點的是:interrupt() 方法并不像在 for 循環(huán)語句中使用 break 語句那樣干脆,馬上就停止循環(huán)。調(diào)用 interrupt() 方法僅僅是在當(dāng)前線程中打一個停止的標(biāo)記,并不是真的停止線程。
也就是說,線程中斷并不會立即終止線程,而是通知目標(biāo)線程,有人希望你終止。至于目標(biāo)線程收到通知后會如何處理,則完全由目標(biāo)線程自行決定。這一點很重要,如果中斷后,線程立即無條件退出,那么我們又會遇到 stop() 方法的老問題。
事實上,如果一個線程不能被 interrupt,那么 stop 方法也不會起作用。
我們來看一個使用 interrupt() 的例子:
public class InterruptThread1 extends Thread{
public static void main(String[] args) {
try {
InterruptThread1 t = new InterruptThread1();
t.start();
Thread.sleep(200);
t.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
super.run();
for(int i = 0; i <= 200000; i++) {
System.out.println("i=" + i);
}
}
}
輸出:
從輸出的結(jié)果我們會發(fā)現(xiàn) interrupt 方法并沒有停止線程 t 中的處理邏輯,也就是說即使 t 線程被設(shè)置為了中斷狀態(tài),但是這個中斷并不會起作用,那么該如何停止線程呢?
這就需要使用到另外兩個與線程中斷有關(guān)的方法了:
public boolean Thread.isInterrupted() //判斷是否被中斷
public static boolean Thread.interrupted() //判斷是否被中斷,并清除當(dāng)前中斷狀態(tài)
這兩個方法使得當(dāng)前線程能夠感知到是否被中斷了(通過檢查標(biāo)志位)。
所以如果希望線程 t 在中斷后停止,就必須先判斷是否被中斷,并為它增加相應(yīng)的中斷處理代碼:
@Override
public void run() {
super.run();
for(int i = 0; i <= 200000; i++) {
//判斷是否被中斷
if(Thread.currentThread().isInterrupted()){
//處理中斷邏輯
break;
}
System.out.println("i=" + i);
}
}
輸出結(jié)果,for 循環(huán)在執(zhí)行完成前就提前結(jié)束了:
在上面這段代碼中,我們增加了 Thread.isInterrupted() 來判斷當(dāng)前線程是否被中斷了,如果是,則退出 for 循環(huán),結(jié)束線程。
這種方式看起來與之前介紹的“使用標(biāo)志位終止線程”非常類似,但是在遇到 sleep() 或者 wait() 這樣的操作,我們只能通過中斷來處理了。
public static native void sleep(long millis) throws InterruptedException
Thread.sleep() 方法會拋出一個 InterruptedException 異常,當(dāng)線程被 sleep() 休眠時,如果被中斷,這會就拋出這個異常。
(注意:Thread.sleep() 方法由于中斷而拋出的異常,是會清除中斷標(biāo)記的。)
總結(jié)
以上是生活随笔為你收集整理的jar 退出run_Java终止线程的三种方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 收入高为什么信用卡额度那么低
- 下一篇: java下拉列表 动态_【示例】教你简单