中断是计算机中一个很大的概念,被广泛用在各种领域中。这里主要介绍下java语言中线程的中断以及InterruptedException异常。
在java语言里,这一逻辑其实很简单。java的线程中断api只是为该线程打上一个中断的标记,并不能真正的中断一个线程。如果想要实现中断操作,需要业务去根据标记做编程。大多数情况下,中断的操作就是抛出一个InterruptedException的异常。当然抛出这个异常,需要由业务来完成。
线程的中断
中断api:
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();
}
private native void interrupt0();
调用thread的interrupt方法,就会为当前线程设置中断标记。其最终实现是native的。
检测中断标记api:
有两个方法:
静态
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
非静态
public boolean isInterrupted() {
return isInterrupted(false);
}
区别在于:
1)interrupted()是静态方法,当然最终也会调用currentThread拿到当前的线程对象;
2)interrupted()会清除当前线程的中断标记,而isInterrupted则不会。
二者最终都会进入下面的方法:
/**
* 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);
也是一个native方法,入参可以指定是否清除标记。
InterruptedException异常
public
class InterruptedException extends Exception {
private static final long serialVersionUID = 6700697376100628473L;
/**
* Constructs an <code>InterruptedException</code> with no detail message.
*/
public InterruptedException() {
super();
}
/**
* Constructs an <code>InterruptedException</code> with the
* specified detail message.
*
* @param s the detail message.
*/
public InterruptedException(String s) {
super(s);
}
}
该异常是一个Exception的子类,说明这是一个checked异常,需要写入签名,并try-catch。大多数情况下,这个异常就是被中断的实际行为的体现。
一些api,通常是阻塞api会抛出这个异常,表明被中断。比如说sleep函数:
public static native void sleep(long millis) throws InterruptedException;
当然这也是一个native的方法。
或者说future的get方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
可以看到其内部实现调用了Thread的方法检测是否有中断标记,如果有就抛出InterruptedException异常。
自己写一个处理中断的例子:
class Task{
public void run() throws InterruptedException {
while(true){
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
Thread.sleep(1000);
System.out.println("running");
}
}
}
public class Main {
public static void main(String args[]) {
Thread t1 = new Thread(() -> {
try {
new Task().run();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(5000);
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
}
}
总结:
java线程中断只是为线程打上标记,其他的不会去做。真正的中断行为需要业务代码去实现。
java中有关线程中断的api有三个。
中断通常与阻塞api有关,中断的过程通常可以是抛出一个InterruptedException。