引言
Java中线程的中断可能与我们想象的不太一样,在线程处于不同的状态时对该线程进行中断操作会有不同的结果。这篇文章,我们来看一下Java中线程的中断。
Thread中断API详解
public void interrupt() {}
public static boolean interrupted() {}
public boolean isInterrupted() {}
private native boolean isInterrupted(boolean ClearInterrupted);
第二个方法是static方法,其他三个都是实例方法。第四个方法是native方法,并且不能被外部访问,实际上,前三个方法都是通过第四个方法实现的。我们先来看这个方法:
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
根据注释,这个方法用来测试某些线程是否被中断。参数ClearInterrupted用来指定是否要对该线程的中断标志进行重置。如果是true,就表示要重置中断标志(重置就是将中断标志恢复为默认值,即没有被中断),否则就不重置。
我们再看实例方法isInterrupted():
public boolean isInterrupted() {
return isInterrupted(false);
}
这个方法调用了上面的native方法并且参数总是false,也就是不重置中断状态。不重置也就对中断状态没有影响。
再看静态的interrupted()方法:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
这个方法是对当前线程调用native方法isInterrupted,但是传入的参数是true,也就是说,这个方法会重置当前线程的中断状态。如果我们连续调用这个方法两次,那么会返回false。
最后来看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();
}
这同样是一个实例方法,也就是说,我们在中断某个线程时,需要指定中断哪个线程。中断并不意味着线程的执行会被中断,实际上,如果是被中断的线程正常运行中,那么对其调用中断操作只会设置它的中断状态为true,线程仍然会正常运行。
我们来看一个例子:
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("线程在运行");
}
}
},"interrupted_thread");
thread.start();
thread.interrupt();
}
}
thread线程被main线程执行了中断操作,但是还会一直运行,一直会输出”线程在运行“。
上面只是线程在正常运行的情况,但是会有一些特殊情况,这些特殊情况包括:
- 线程执行了Object.wait()、Object.wait(long)和Object.wait(long,int)方法。
- 线程执行了Thread.join()、Thread.join(long)和Thread.join(long,int)方法。
- 线程执行了sleep(long)、sleep(long、int)方法。
当线程已经执行了这些方法,再调用interrupt()方法对其进行中断操作时,会抛出InterruptedException方法,然后线程的中断状态会被清除。
看下面的例子:
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"interrupted_thread");
thread.start();
thread.interrupt();
}
}
thread线程调用了sleep方法处于TIMED-WAITING状态,然后main线程对其进行了中断操作,此时thread就会抛出InterruptedException异常,运行结果如下:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at person.andy.concurrency.thread.Interrupt$1.run(Interrupt.java:9)
at java.lang.Thread.run(Thread.java:748)
Thread.join()方法和Object.wait()方法也是同样的机制,下面是一个wait的示例:
public class Interrupt {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"interrupted_thread");
thread.start();
Thread.sleep(3000);
thread.interrupt();
}
}
同样会抛出异常。像Thread.sleep(long)、Thread.join()和Object.wait()这类方法,是能响应中断的方法,我们看它们的声明,会发现它们都抛出了java.lang.InterruptedException异常。
正确使用中断
既然其他线程调用interrupt()并不能使线程真正的中断,那么我们能用中断来做什么呢?我们可以由被中断的线程自己不断地测试中断状态(调用isInterrupted()方法),如果被中断了,就自己停止做某些事情,或者说,如果没被中断,就一直做某些事情,看下面的例子:
public class Interrupt {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("interrupted_thread没有被中断");
}
System.out.println("interrupted_thread被中断了");
}
},"interrupted_thread");
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
interrupted_thread在while循环中不断地判断自己有没有被中断,如果没有,就继续运行,否则,跳出循环。
这么来看,中断实现了任务的取消,通常,中断是实现取消的最合理方式。