天天看点

java中断机制

1. 简述

看了很多篇关于java中断机制的博文,这里用自己的语言简单总结下,希望可以方便大家快速理解。

详细请参考:

http://www.infoq.com/cn/articles/java-interrupt-mechanism

http://www.cnblogs.com/carmanloneliness/p/3516405.html

2. 线程的关闭

线程的关闭有三种方法:

  1. Thread.stop()。不安全,已过时。参考: http://blog.csdn.net/kingzma/article/details/45739963
  2. 设置标志位,控制线程关闭,但不能做到对阻塞线程或者运行时间很长的方法的关闭。
  3. interrupt()方法关闭,对于阻塞线程,会“几乎”立即关闭。对于非阻塞线程,需要通过调用isInterrupted()方法实时监测中断标志位来手动地关闭线程(原理同2)。

本文主要从方法三入手,从源代码角度,详细说明java中断机制。

3. 中断简述

java中断是一种协作式的中断,也就是说,调用interrupt()方法并不代表着直接中断该线程。而仅仅是更改了“中断标志位”,具体是否中断由线程决定。

关于中断,Thread类中有如下几个方法:

方法名 功能
public void interrupt() 中断线程
public static boolean interrupted() 测试当前线程是否已经中断
public boolean isInterrupted() 测试线程是否已经中断
private native boolean isInterrupted(boolean ClearInterrupted) 测试线程是否已经中断并选择是否重置中断标志

3.1 private volatile Interruptible blocker成员

在详细讲解上述四种方法之前,首先了解一下Thread类中的一个重要成员:private volatile Interruptible blocker。是的,这个blocker对象,便是所谓的“中断标志位”。关于这个中断标志位,有如下三个操作:

  1. 默认blocker=null; 
  2. 调用方法“interrupt0();”将会导致“该线程的中断状态将被设置(JDK文档中术语)”。
  3. 再次调用“interrupt0();”将会导致“其中断状态将被清除(同JDK文档中术语)”

通过反复调用interrupt0()方法,会将blocker的值在true和false之间来回设置。

另外,Interruptible类中也有一个interrupt()方法,没有找到该方法的源码。根据博文 Java多线程之interrupt()的深度研究,该方法负责真正地打断线程,并且跑出InterruptedException异常,这一点会在下面详述。

3.2 isInterrupted(boolean ClearInterrupted)方法

这个方法是个私有的native方法,主要用于Thread类中的其他方法调用。

检测中断标志位,判断线程是否被中断。根据传入参数,决定是否重置中断标志位。如果参数为true,则重置中断标志位为false,否则不改变中断标志位。

3.3 public boolean isInterrupted()方法

测试线程是否已经中断。

线程的中断状态不受该方法的影响

。线程中断被忽略,因为在中断时不处于活动状态的线程将由此返回 false 的方法反映出来。

public boolean isInterrupted() {
        return isInterrupted(false);
    }
           

源码即调用isInterrupted(false),不改变中断标志位。

3.4 public static boolean interrupted()方法

这是一个静态方法,所以它仅能执行该方法的语句所在的线程,与调用该方法的对象无关。具体可参考我的另一篇小文章 由sleep()谈多线程中的静态方法 。

interrupted()方法测试“当前”线程是否已经中断,

线程的中断状态 由该方法清除

,线程中断被忽略,因为在中断时不处于活动状态的线程将由此返回 false 的方法反映出来。

public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
           

从源码便可以看出,该方法仅能控制当前线程,值得一提的是currentThread()方法也是一个静态方法。

不难理解,连续调用两次interrupted()方法之后,其返回值一定是false。

初始状态 - 第一次调用 - 第二次调用
中断标志位:true 调用interrupted() 中断标志位:false 第二次调用 中断标志位:false
返回值:true 返回值:false
中断标志位:false 调用interrupted() 中断标志位:false 第二次调用 中断标志位:false
返回值:false 返回值:false

3.5 public void 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();
    }
           

下面用两张图分别解释为什么

1.调用interrupt()方法并不会中断一个正在运行的线程.

2.若调用sleep()而使线程处于阻塞状态,这时调用interrupt()方法,会抛出InterruptedException,从而使线程提前结束阻塞状态,退出阻塞代码。

4. 一个疑问(可略过)

下面是笔者测试代码的时候遇到的一个问题,如果读者有兴趣烦请不吝赐教。

4.1 测试代码一:

public class Test1 {
    private static Object obj;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("中断") {
            public void run() {
                try {
                        sleep(*);
                } catch (InterruptedException e) {

                }
            }
        };
        t1.start();
        //Thread.sleep(1);
        System.out.println(t1.isInterrupted());
        t1.interrupt();
        System.out.println(t1.isInterrupted());
        Thread.sleep();
        System.out.println(t1.isInterrupted());
    }
}
           

测试结果:false,true,false。

不难理解,调用interrupt( )之后,由于t1线程是阻塞线程,所以其blocker被置为true,主线程sleep(50)的时间内,完成了线程的停止工作,blocker为false。

4.2 测试代码二:

将测试代码一种的注释行取消注释,即在t1.start()之后,让主线程sleep(1)。

测试结果:false,true,false。

4.3 测试代码三:

将测试代码二中的sleep(1)改为sleep(100)。

测试结果:false,false,false。

疑问:t1.interrupt()语句是在Thread.sleep()语句之后执行的,为什么睡眠时间会对下一句的中断状态产生影响?

本人测试机器较卡,在另一台机器上,使用sleep(1)测试出来的结果也是false,false,false。

希望可以得到帮助,教学相长。