线程中断理解

无法中断的线程

package objective1.action2;

public class InterruptHandler {

    public static void main(String[] args) {
        Runnable runnable = new InterruptRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        thread.interrupt();
    }

}

class InterruptRunner implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            System.out.println("i=" + i++);
        }
    }
}

该例子尝试使用用interrupt中断线程,但interrupt方法并不像break方法一样,马上停止循环,而是持续对i增加,不会停止。

中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的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();
    }

从上面源码中也可与看出,interrupt只是在当前线程中打了个停止标志。
那么如何才能停止线程呢?
可以在该线程中观察中断标志状态,如果中断标志状态显示中断,就可以在该线程中执行中断。

判断线程中断的状态

Thread提供了两种方法判断线程中断状态。

 public static boolean interrupted()
 public boolean isInterrupted()

两个方法有什么区别呢?
interrupted方法: 测试当前线程是否已经是中断状态,并清除标志位
isInterrupted方法:测试当前线程是否已经是中断状态,不清除标志位

  • isInterrupted()方法
public class InterruptHandler2 {

    public static void main(String[] args) {
        Thread thread = new InterruptThread();
        thread.start();
        thread.interrupt();
    }

}

class InterruptThread extends Thread {
    @Override
    public void run() {
        System.out.println("停止状态1:" + Thread.currentThread().isInterrupted());
        System.out.println("停止状态2:" + Thread.currentThread().isInterrupted());
        System.out.println("停止状态3:" + Thread.currentThread().isInterrupted());
    }
}
停止状态1true
停止状态2true
停止状态3true

可以看出isInterrupted()判断状态后,再次判断中断状态仍然是true,没有清除中断标志位。

  • interrupted方法
public class InterruptHandler2 {

    public static void main(String[] args) {
        Thread thread = new InterruptThread1();
        thread.start();
        thread.interrupt();

    }
}

class InterruptThread1 extends Thread {
    @Override
    public void run() {
        System.out.println("停止状态1:" + Thread.interrupted());
        System.out.println("停止状态2:" + Thread.interrupted());
        System.out.println("停止状态3:" + Thread.interrupted());
    }
}
停止状态1true
停止状态2false
停止状态3false

可以看出interrupted()判断状态后,再次判断中断状态全是false,说明将标志位恢复了。

  • 源码分析

观察Thread源码,发现这两个方法最终都是调用 isInterrupted(boolean ClearInterrupted),不同的只是interrupted()默认将清除中断标志位设成true.

   public boolean isInterrupted() {
        return isInterrupted(false);
    }
   public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
 private native boolean isInterrupted(boolean ClearInterrupted);

停止线程的方法

使用异常停止

使用break停止

判断中断标志位,当标志位被置为中断时,使用break中断。虽然中断了while循环但是,还是继续往下执行了,也就是说线程没有立即停止。

public class InterruptHandler {

    public static void main(String[] args) {
        System.out.println("start");
        Runnable runnable = new InterruptRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("end");
    }

}

class InterruptRunner implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("已经是中断状态了,我要退出了");
                break;
            }
            System.out.println("我还没中断 i=" + i++);

        }
        System.out.println("我在while下面!我被执行说明break后代码继续运行,线程没有立即停止");
    }
}
....
我还没中断 i=3
end
已经是中断状态了,我要退出了
我在while下面!我被执行说明break后代码继续运行,线程没有立即停止

使用在sleep中catch异常停止

public class InterruptHandler {

    public static void main(String[] args) {
        System.out.println("start");
        Runnable runnable = new InterruptRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("end");
    }

}

class InterruptRunner implements Runnable {

    @Override
    public void run() {
        int i = 0;
        try {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("已经是中断状态了,我要退出了");
                    Thread.sleep(100);
                }
                System.out.println("我还没中断 i=" + i++);

            }
        } catch (InterruptedException e) {
            System.out.println("中断标志" + Thread.currentThread().isInterrupted());
            System.out.println("进入" + Thread.currentThread().getName() + "的exception方法,然后中断");
            System.out.println(e);
            Thread.currentThread().interrupt();
        }

    }
}
我还没中断 i=19
end
已经是中断状态了,我要退出了
catch exception后中断标志false
进入Thread-0exception方法,然后中断
java.lang.InterruptedException: sleep interrupted

可以看出在线程sleep时候,存在中断操作会抛出InterruptedException ,并且将清除中断标志位停止状态。而这时若catch住异常,并对线程执行interrupt操作,就可以达到中断线程的效果。

Q:那么为什么在线程sleep的时候,执行中断操作(或中断时候执行sleep)会抛出异常呢?
当thread被阻塞的时候,比如被Object.wait,Thread.join和Thread.sleep三种方法之一阻塞时。调用它的interrput()方法。 可想而知,没有占用CPU运行的线程是不可能给自己的中断状态置位的。这就会产生一个InterruptedException异常。

使用抛出异常在外面catch停止

public class InterruptHandler {

    public static void main(String[] args) {
        System.out.println("start");
        Runnable runnable = new InterruptRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("end");
    }

}

class InterruptRunner implements Runnable {

    @Override
    public void run() {
        int i = 0;
        try {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("已经是中断状态了,我要退出了");
                    throw new InterruptedException();
                }
                System.out.println("我还没中断 i=" + i++);

            }
        } catch (InterruptedException e) {
            System.out.println("中断标志" + Thread.currentThread().isInterrupted());
            System.out.println("进入" + Thread.currentThread().getName() + "的exception方法,然后中断");
            System.out.println(e);
        }

    }
}
我还没中断 i=57
end
已经是中断状态了,我要退出了
中断标志true
进入Thread-0exception方法,然后中断
java.lang.InterruptedException

如上例子判断中断标志位为true时,立即将异常抛出,并在外层处理达到线程停止。这样做的好处是,还可以将异常往外抛,使线程停止的事件得以传播。

使用return的方法停止

public class InterruptHandler {

    public static void main(String[] args) {
        System.out.println("start");
        Runnable runnable = new InterruptRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("end");
    }

}

class InterruptRunner implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("已经是中断状态了,我要退出了");
                return;
            }
            System.out.println("我还没中断 i=" + i++);
        }
    }
}
start
我还没中断 i=0
end
已经是中断状态了,我要退出了

可以看出,其实和用异常方法停止线程没有本质区别,都是在判断中断标志位被设置为true时,进行退出操作。

使用过期的stop方法停止

stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。不建议使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章