無法中斷的線程
package objective1.action2;
public class InterruptHandler {
public static void main(String[] args) {
Runnable runnable = new InterruptRunner();
Thread thread = new Thread(runnable);
thread.start();
thread.interrupt();
}
}
class InterruptRunner implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
System.out.println("i=" + i++);
}
}
}
該例子嘗試使用用interrupt中斷線程,但interrupt方法並不像break方法一樣,馬上停止循環,而是持續對i增加,不會停止。
中斷可以理解爲線程的一個標識位屬性,它表示一個運行中的線程是否被其他線程進行了中斷操作。中斷好比其他線程對該線程打了個招呼,其他線程通過調用該線程的interrupt()
方法對其進行中斷操作。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
從上面源碼中也可與看出,interrupt只是在當前線程中打了個停止標誌。
那麼如何才能停止線程呢?
可以在該線程中觀察中斷標誌狀態,如果中斷標誌狀態顯示中斷,就可以在該線程中執行中斷。
判斷線程中斷的狀態
Thread提供了兩種方法判斷線程中斷狀態。
public static boolean interrupted()
public boolean isInterrupted()
兩個方法有什麼區別呢?
interrupted方法: 測試當前線程是否已經是中斷狀態,並清除標誌位
isInterrupted方法:測試當前線程是否已經是中斷狀態,不清除標誌位
- isInterrupted()方法
public class InterruptHandler2 {
public static void main(String[] args) {
Thread thread = new InterruptThread();
thread.start();
thread.interrupt();
}
}
class InterruptThread extends Thread {
@Override
public void run() {
System.out.println("停止狀態1:" + Thread.currentThread().isInterrupted());
System.out.println("停止狀態2:" + Thread.currentThread().isInterrupted());
System.out.println("停止狀態3:" + Thread.currentThread().isInterrupted());
}
}
停止狀態1:true
停止狀態2:true
停止狀態3:true
可以看出isInterrupted()判斷狀態後,再次判斷中斷狀態仍然是true,沒有清除中斷標誌位。
- interrupted方法
public class InterruptHandler2 {
public static void main(String[] args) {
Thread thread = new InterruptThread1();
thread.start();
thread.interrupt();
}
}
class InterruptThread1 extends Thread {
@Override
public void run() {
System.out.println("停止狀態1:" + Thread.interrupted());
System.out.println("停止狀態2:" + Thread.interrupted());
System.out.println("停止狀態3:" + Thread.interrupted());
}
}
停止狀態1:true
停止狀態2:false
停止狀態3:false
可以看出interrupted()判斷狀態後,再次判斷中斷狀態全是false,說明將標誌位恢復了。
- 源碼分析
觀察Thread源碼,發現這兩個方法最終都是調用 isInterrupted(boolean ClearInterrupted),不同的只是interrupted()默認將清除中斷標誌位設成true.
public boolean isInterrupted() {
return isInterrupted(false);
}
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
private native boolean isInterrupted(boolean ClearInterrupted);
停止線程的方法
使用異常停止
使用break停止
判斷中斷標誌位,當標誌位被置爲中斷時,使用break中斷。雖然中斷了while循環但是,還是繼續往下執行了,也就是說線程沒有立即停止。
public class InterruptHandler {
public static void main(String[] args) {
System.out.println("start");
Runnable runnable = new InterruptRunner();
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("end");
}
}
class InterruptRunner implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("已經是中斷狀態了,我要退出了");
break;
}
System.out.println("我還沒中斷 i=" + i++);
}
System.out.println("我在while下面!我被執行說明break後代碼繼續運行,線程沒有立即停止");
}
}
....
我還沒中斷 i=3
end
已經是中斷狀態了,我要退出了
我在while下面!我被執行說明break後代碼繼續運行,線程沒有立即停止
使用在sleep中catch異常停止
public class InterruptHandler {
public static void main(String[] args) {
System.out.println("start");
Runnable runnable = new InterruptRunner();
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("end");
}
}
class InterruptRunner implements Runnable {
@Override
public void run() {
int i = 0;
try {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("已經是中斷狀態了,我要退出了");
Thread.sleep(100);
}
System.out.println("我還沒中斷 i=" + i++);
}
} catch (InterruptedException e) {
System.out.println("中斷標誌" + Thread.currentThread().isInterrupted());
System.out.println("進入" + Thread.currentThread().getName() + "的exception方法,然後中斷");
System.out.println(e);
Thread.currentThread().interrupt();
}
}
}
我還沒中斷 i=19
end
已經是中斷狀態了,我要退出了
catch exception後中斷標誌false
進入Thread-0的exception方法,然後中斷
java.lang.InterruptedException: sleep interrupted
可以看出在線程sleep時候,存在中斷操作會拋出InterruptedException ,並且將清除中斷標誌位停止狀態。而這時若catch住異常,並對線程執行interrupt操作,就可以達到中斷線程的效果。
Q:那麼爲什麼在線程sleep的時候,執行中斷操作(或中斷時候執行sleep)會拋出異常呢?
當thread被阻塞的時候,比如被Object.wait,Thread.join和Thread.sleep三種方法之一阻塞時。調用它的interrput()方法。 可想而知,沒有佔用CPU運行的線程是不可能給自己的中斷狀態置位的。這就會產生一個InterruptedException異常。
使用拋出異常在外面catch停止
public class InterruptHandler {
public static void main(String[] args) {
System.out.println("start");
Runnable runnable = new InterruptRunner();
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("end");
}
}
class InterruptRunner implements Runnable {
@Override
public void run() {
int i = 0;
try {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("已經是中斷狀態了,我要退出了");
throw new InterruptedException();
}
System.out.println("我還沒中斷 i=" + i++);
}
} catch (InterruptedException e) {
System.out.println("中斷標誌" + Thread.currentThread().isInterrupted());
System.out.println("進入" + Thread.currentThread().getName() + "的exception方法,然後中斷");
System.out.println(e);
}
}
}
我還沒中斷 i=57
end
已經是中斷狀態了,我要退出了
中斷標誌true
進入Thread-0的exception方法,然後中斷
java.lang.InterruptedException
如上例子判斷中斷標誌位爲true時,立即將異常拋出,並在外層處理達到線程停止。這樣做的好處是,還可以將異常往外拋,使線程停止的事件得以傳播。
使用return的方法停止
public class InterruptHandler {
public static void main(String[] args) {
System.out.println("start");
Runnable runnable = new InterruptRunner();
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("end");
}
}
class InterruptRunner implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("已經是中斷狀態了,我要退出了");
return;
}
System.out.println("我還沒中斷 i=" + i++);
}
}
}
start
我還沒中斷 i=0
end
已經是中斷狀態了,我要退出了
可以看出,其實和用異常方法停止線程沒有本質區別,都是在判斷中斷標誌位被設置爲true時,進行退出操作。
使用過期的stop方法停止
stop()方法在終結一個線程時不會保證線程的資源正常釋放,通常是沒有給予線程完成資源釋放工作的機會,因此會導致程序可能工作在不確定狀態下。不建議使用。