几个API
判断线程是否被中断
while(!Thread.currentThread().isInterrupted() && more work to do){
do more work
}
JDK源代码分析
public void interrupt() {
//如果不是本线程,那么需要检查是否拥有权限。如果没有,抛出SecurityException
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {//本线程对象所持有的锁,用于防止并发出错。
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag(设置标识位,native方法)
b.interrupt(this);//真正打断进程的方法
return;
}
}
interrupt0();
}
// 静态方法,这个方法有点坑,调用该方法调用后会清除中断状态。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 这个方法不会清除中断状态
public boolean isInterrupted() {
return isInterrupted(false);
}
// 上面两个方法会调用这个本地方法,参数代表是否清除中断状态
private native boolean isInterrupted(boolean ClearInterrupted);
中断机制
如果线程被interrupt,大概有这么几种情况。
-
如果线程堵塞在object.wait、Thread.join和Thread.sleep,将会清除线程的中断状态,并抛出InterruptedException;
-
如果线程堵塞在java.nio.channels.InterruptibleChannel的IO上,Channel将会被关闭,线程被置为中断状态,并抛出java.nio.channels.ClosedByInterruptException;
-
如果线程堵塞在java.nio.channels.Selector上,线程被置为中断状态,select方法会马上返回,类似调用wakeup的效果;
-
如果不是以上三种情况,thread.interrupt()方法仅仅是设置线程的中断状态为true。
下面代码中的t1线程将永远不会被中断。程序永远不会退出。
public class ThreadInterruptTest {
static long i = 0;
public static void main(String[] args) {
System.out.println("begin");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (true){
i++;
System.out.println(String.valueOf(i));
}
}
});
t1.start();
t1.interrupt();
}
}
java的线程中断,在线程没有阻塞的情况下,是通过标识位实现的,依赖于程序员自己检查。那么,对于阻塞的线程,JVM又是如何处理的呢?
很简单,就是设置中断状态,然后唤醒线程。让线程重新工作而已。
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
//获取本地线程对象
OSThread* osthread = thread->osthread();
if (!osthread->interrupted()) {//判断本地线程对象是否为中断
osthread->set_interrupted(true);//设置中断状态为true
// More than one thread can get here with the same value of osthread,
// resulting in multiple notifications. We do, however, want the store
// to interrupted() to be visible to other threads before we execute unpark().
//这里是内存屏障,内存屏障的目的是使得interrupted状态对其他线程立即可见
OrderAccess::fence();
//_SleepEvent相当于Thread.sleep,表示如果线程调用了sleep方法,则通过unpark唤醒
ParkEvent * const slp = thread->_SleepEvent ;
if (slp != NULL) slp->unpark() ;
}
// For JSR166. Unpark even if interrupt status already was set
if (thread->is_Java_thread())
((JavaThread*)thread)->parker()->unpark();
//_ParkEvent用于synchronized同步块和Object.wait(),这里相当于也是通过unpark进行唤醒
ParkEvent * ev = thread->_ParkEvent ;
if (ev != NULL) ev->unpark() ;
}
中断的使用
中断是实现任务取消的最合理的方式。在广泛接受的编程规范,中断处理应该尽可能迅速。一般而言,我们提供的库不应该捕获中断,而是应该直接抛出中断让调用者决定。