栋哥带你学Java线程中断

线程中断

(一)stop方法

在早期,线程中又一个stop方法,可以直接终止线程.但是现在已经不用了,而且api上也已经在方法下标明已过时.这时因为stop方法一旦调用,不管线程是什么状态的都直接会被终
止,假如正在运行线程,run方法中的代码执行到一半被中断了,那么如果是像银行这些地
方,会造成不可预计的事情发生,所以是很危险的.

(二)interrupt方法

既然stop方法不能使用,那么我就去api看看还有没有别的方法,然后api中有这个方法
interrupt()中断线程。那么我们来测试一下.
我们先写一个类实现Runnable接口,然后重写run方法,在run方法里写个死循环打印任意
内容,再使用interrupt看看能不能中断.
public class Test {
    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        Thread t = new Thread(runnable);
        t.start();
        t.interrupt();
    }
}

class TestRunnable implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("...");
        }
    }
}
结果:一直在循环打印...
那么这时候就该想想是不是自己的代码有问题.然后我又去api找方法,这时候很巧,看到了
isInterrupted方法,它是用来判断线程是否已经中断.
接着我们在main函数里加上这几句.
boolean interrupted = t.isInterrupted();
System.out.println(interrupted);
if (interrupted == false) {
    t.interrupt();
}
isInterrupted()是个布尔类型的方法,线程如果是运行状态的话,我们输出一下结果,
发现返回的是false,返回false的时候说明该线程没有中断.那我们就加个if判断,如果
返回false就是线程还没中断,我们就调用interrupt()方法中断线程,再次运行.
运行结果:发现还是死循环打印.
这时候不管你是什么心态,既然api上有那就是这个方法是可以用的,只是我们的测试方式
没到位,我们再去看文档.点进去interrupt()方法.
public void interrupt()
中断线程。
如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
如果以前的条件都没有保存,则该线程的中断状态将被设置。
中断一个不处于活动状态的线程不需要任何作用。
抛出:SecurityException - 如果当前线程无法修改该线程
文档里说了这么一大堆,我们挑重点来看.线程在调用Object类的wait、join、sleep方
法后再去调用interrupt方法,那么就会清除它们的中断状态,并且报一个中断异常----
InterruptedException.
这时候把代码改成我现在这个样子⬇️ :
public class Test {
    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        Thread t = new Thread(runnable);
        t.start();
        boolean interrupted = t.isInterrupted();
        System.out.println(interrupted);
        if (interrupted == false) {
            t.interrupt();
            boolean interrupted2 = t.isInterrupted();
            System.out.println(interrupted2);
        }
    }
}

class TestRunnable implements Runnable {
    private int test = 10;
    @Override
    public void run() {
        while (test != 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("...");
            test--;
        }
    }
}
输出结果:
false
true
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at coam.lanou3g.TestRunnable.run(Test.java:25)
    at java.lang.Thread.run(Thread.java:748)
...
...
...
...
...
...
...
...
...
...
解析:根据文档的说法,我在在使用了sleep方法后确实报了中断异常,并且异常的右边明
确指出是sleep interrupted,然后我们再看main函数里,我在线程启动后调用了一次
isInterrupted方法,然后再if里面调用了interrupt中断方法后,再次调用isInterrupted
方法,然后发现变成了ture.那么我们就可以确定,调用interrupt方法清除状态就是改变
isInterrupted方法的布尔值.既然有了这个结论我们就再改一次代码.
public static void main(String[] args) {
    TestRunnable runnable = new TestRunnable();
    Thread t = new Thread(runnable);
    t.start();
    System.out.println("线程启动啦");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    t.interrupt();
    System.out.println("线程关闭啦");
}
class TestRunnable implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("...");
        }
    }
}
输出结果:
线程启动啦
...
线程关闭啦
然后我们就会发现这次是真的被我们关闭啦.
解析:因为一开始线程启动的时候,isInterrupted返回的是false,但是while只有为
true的时候才执行while里面的代码,所以我们在main函数里把中断方法interrupt加
上就可以改变它的值,while(false)不执行线程就结束.
结论:实际上这个interrupt()方法是用来设置isInterrupted方法的返回值.如果线程
中有wait()、sleep()、join()等方法,这时调用interrupt方法就会抛出一个异常
InterruptedException,并且清除中断状态,如果线程中没有出现这几个方法,那么这
时调用interrupt方法,就直接设置中断状态(true/false的改变).

(三)使用标记法

先上代码再解析.
public class Test {
    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        Thread t = new Thread(runnable);
        t.start();
        System.out.println("线程启动啦");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        runnable.isOver = false;
        System.out.println("线程关闭啦");
    }
}

class TestRunnable implements Runnable {
    public boolean isOver = true;
    @Override
    public void run() {
        while (isOver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("...");
        }
    }
}
运行结果:
线程启动啦
...
线程关闭啦
...
解析:标记法中断方法有点类似,都是靠一个值的改变来终止线程.这段代码里,测试类里面
定义了成员变量isOver并设置初始值为true,是为了执行while里面的代码.然后在run
方法里面让线程沉睡一秒钟,主要是不睡眠打印出来太多东西了,然后在main函数里,先开
启线程,然后打印线程启动(方便观看效果),继续让线程沉睡一秒,然后把TestRunnable
类里的成员变量isOver设为false,类里用public修饰是方便直接调用就不设为私有了,
测试的时候不用那么麻烦,然后我们再看效果.可以能有些人会不一样,第二行的...没有,
这是正常的,这要看是线程进入run方法后先结束睡眠还是main函数里先修改类的成员有
关.

interrupt方法和标记法的使用场合
一般我们首选标记法来终止线程,interrupt方法能不使用就尽量不要使用,如果要停止线程,直接使用标记法.但是如果遇到了等待状态时,可以使用interrupt方法,强行清楚该状态interrupt方法.

你可能感兴趣的:(Java)