线程中断理解

来源:互联网 发布:均值漂移算法原理 编辑:程序博客网 时间:2024/06/16 08:01

无法中断的线程

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=3end已经是中断状态了,我要退出了我在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=19end已经是中断状态了,我要退出了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=57end已经是中断状态了,我要退出了中断标志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=0end已经是中断状态了,我要退出了

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

使用过期的stop方法停止

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

0 0
原创粉丝点击