无法中断的线程
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()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。不建议使用。