Java中断机制剖析

Java中断机制剖析

首先,看看Thread类里的几个方法:

public static boolean interrupted 测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

public boolean isInterrupted ()

测试线程是否已经中断。线程的中断状态 不受该方法的影响。

public void interrupt ()

中断线程。

上面列出了与中断有关的几个方法及其行为,可以看到interrupt是中断线程。如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。

其实,Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(不一定就是对象的属性,事实上,该状态也确实不是Thread的字段),interrupt方法仅仅只是将该状态置为true。如下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestInterrupt {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
t.interrupt();
System.out.println( "已调用线程的interrupt方法" );
}
static class MyThread extends Thread {
public void run() {
int num = longTimeRunningNonInterruptMethod( 2 , 0 );
System.out.println( "长时间任务运行结束,num=" + num);
System.out.println( "线程的中断状态:" + Thread.interrupted());
}
private static int longTimeRunningNonInterruptMethod( int count, int initNum) {
for ( int i= 0 ; i for ( int j= 0 ; j initNum ++;
}
}
return initNum;
}
}
}

一般情况下,会打印如下内容:
已调用线程的interrupt方法
长时间任务运行结束,num=-2
线程的中断状态:true

可见,interrupt方法并不一定能中断线程。但是,如果改成下面的程序,情况会怎样呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.TimeUnit;
public class TestInterrupt {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
t.interrupt();
System.out.println( "已调用线程的interrupt方法" );
}
static class MyThread extends Thread {
public void run() {
int num = - 1 ;
try {
num = longTimeRunningInterruptMethod( 2 , 0 );
} catch (InterruptedException e) {
System.out.println( "线程被中断" );
throw new RuntimeException(e);
}
System.out.println( "长时间任务运行结束,num=" + num);
System.out.println( "线程的中断状态:" + Thread.interrupted());
}
private static int longTimeRunningInterruptMethod( int count, int initNum) throws InterruptedException{
for ( int i= 0 ; i TimeUnit.SECONDS.sleep( 5 );
}
return initNum;
}
}
}

经运行可以发现,程序抛出异常停止了,run方法里的后两条打印语句没有执行。那么,区别在哪里?

一般说来,如果一个方法声明抛出InterruptedException,表示该方法是可中断的(没有在方法中处理中断却也声明抛出InterruptedException的除外),也就是说可中断方法会对interrupt调用做出响应(例如sleep响应interrupt的操作包括清除中断状态,抛出InterruptedException),如果interrupt调用是在可中断方法之前调用,可中断方法一定会处理中断,像上面的例子,interrupt方法极可能在run未进入sleep的时候就调用了,但sleep检测到中断,就是处理该中断。如果在可中断方法正在执行中的时候调用interrupt,会怎么样呢?这就要看可中断方法处理中断的时机了,只要可中断方法能检测到中断状态为true,就应该处理中断。让我们为开头的那段代码加上中断处理。

那么自定义的可中断方法该如何处理中断呢?那就是在适合处理中断的地方检测线程中断状态并处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class TestInterrupt {
public static void main(String[] args) throws Exception {
Thread t = new MyThread();
t.start();
// TimeUnit.SECONDS.sleep(1);//如果不能看到处理过程中被中断的情形,可以启用这句再看看效果
t.interrupt();
System.out.println( "已调用线程的interrupt方法" );
}
static class MyThread extends Thread {
public void run() {
int num;
try {
num = longTimeRunningNonInterruptMethod( 2 , 0 );
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "长时间任务运行结束,num=" + num);
System.out.println( "线程的中断状态:" + Thread.interrupted());
}
private static int longTimeRunningNonInterruptMethod( int count, int initNum) throws InterruptedException {
if (interrupted()) {
throw new InterruptedException( "正式处理前线程已经被请求中断" );
}
for ( int i= 0 ; i for ( int j= 0 ; j initNum ++;
}
//假如这就是一个合适的地方
if (interrupted()) {
//回滚数据,清理操作等
throw new InterruptedException( "线程正在处理过程中被中断" );
}
}
return initNum;
}
}
}

如上面的代码,方法longTimeRunningNonInterruptMethod此时已是一个可中断的方法了。在进入方法的时候判断是否被请求中断,如果是,就不进行相应的处理了;处理过程中,可能也有合适的地方处理中断,例如上面最内层循环结束后。

这段代码中检测中断用了Thread的静态方法interrupted,它将中断状态置为false,并将之前的状态返回,而isInterrupted只是检测中断,并不改变中断状态。一般来说,处理过了中断请求,应该将其状态置为false。但具体还要看实际情形。

猜你喜欢

转载自hai0378.iteye.com/blog/1546623
今日推荐