Java线程的终止Interrupted优化和stop强制终止

Java线程的终止Interrupted优化和stop强制终止

我们知道Java中线程在运行完run()方法之后就会停止,这样的情况是自然停止,那如果我们在run()方法运行的时候想停止线程该怎么做呢?

在Thread类中提供了stop()方法强制停止线程,类似直接将线程kill掉

stop()方法存在的弊端,如下边的例子

public class InterruptedDemo{
    public static void main(String[] args) {
        StopDemo demo = new StopDemo();
        demo.start();
        try {
            //阻塞1秒,保证i自增完毕
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        demo.stop();//停止线程
        demo.sout();//输出i、j自增结果
    }
    private static class StopDemo extends Thread{
        //定义两个变量在run()方法中做自增操作
        private int i = 0;
        private int j = 0;
        @Override
        public void run() {
            i++;
            try {
                //阻塞十秒钟
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
        //输出i/j自增结果
        public void sout(){
            System.out.println("i="+i+",j="+j);
        }
    }
}

输出的结果为
i=1,j=0

从上面的例子看出,如果在线程没有执行完毕就使用stop()方法强制停止线程就有可能导致数据的不一致性,而且Thread.stop()方法现在已经被弃用

Interrupted优化线程中断

Interrupted是针对run()方法中存在无限循环或者线程阻塞做出的一个线程中断的优化方案
无限循环优化
Interrupt案例,如下

public class InterruptedDemo implements Runnable{
    @Override
    public void run() {
        while (true){

        }
    }

    public static void main(String[] args) {
        Thread t = new Thread(new InterruptedDemo());
        t.start();
        try {
            t.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            t.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上边例子中的run()方法中使用了一个while(true)使得线程无限循环下去,但是这样子做的话会使得该线程一直占用CPU资源,如果不需要该线程执行下去是没有办法停止的,除非我们在控制台上直接kill掉,我们可以做一个优化,如下

 volatile boolean interrupted = false;
    @Override
    public void run() {
        while (!interrupted){

        }
    }

在这里设置了一个可见变量,当我们不需要该线程一直运行的话只需要将interrupt设置为true,线程就跳出了循环,线程自然停止
在Java中提供了一个方法Thread.currentThread().isInterrupted() 该方法可以提供一个共享的线程中断的标记,默认值为false;我们可以该方法来代替我们自定义的可见变量

public class InterruptedDemo implements Runnable{
    int i = 0;
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("test:"+i++);
        }
    }

    public static void main(String[] args) {
        Thread t = new Thread(new InterruptedDemo());
        t.start();
        t.interrupt();//将Thread.currentThread().isInterrupted()设置为true从而停止线程
    }
}

线程阻塞优化
在上面Interrupt案例中的main方法中我们看到当调用wait、sleep、join等方法都会抛出一个InterruptedException异常,我们先做一个假设:如果调用wait、sleep、join等方法没有抛出任何异常的情况下,如果我们想中断异常即使调用了interrupt 方法也只能到线程阻塞时间达到超时时间之后再执行完run()方法自然终止线程,假定超时时间有24小时,那线程也只能等24小时之后才可以中断。这样的话就没有意义了;看下边例子

public class InterruptedDemo implements Runnable {

    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            //当这里阻塞了这么多秒时下边main方法调用interrupt方法
            // 将Thread.currentThread().isInterrupted()的值改为true也得等到线程唤醒以后才会执行完毕
            TimeUnit.SECONDS.sleep(100000000);
            
        }
    }
    public static void main(String[] args) {
        InterruptedDemo demo = new InterruptedDemo();
        Thread t = new Thread(demo);
        t.start();
        t.interrupt();
    }
}

当抛出了InterruptedException异常又是怎么处理的呢?看下边代码

public class InterruptedDemo implements Runnable {

    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            try {
                TimeUnit.SECONDS.sleep(100);
            } catch (InterruptedException e) {//在这里InterruptedException触发了线程复位,也就是将Thread.currentThread().isInterrupted()的值改为false
                e.printStackTrace();
                //在这里我们可以决定线程是否中断
            }
        }
        System.out.println("Interrupted run");
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptedDemo demo = new InterruptedDemo();
        Thread t = new Thread(demo);
        t.start();
        t.sleep(1000);//这里让main线程阻塞1秒,保证InterruptedException有足够的时间响应中断
        t.interrupt();
    }
}
输出结果
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.thread.InterruptedDemo.run(InterruptedDemo.java:12)
	at java.lang.Thread.run(Thread.java:748)

上面代码中我们看到当main方法中t调用了interrupt方法后将Thread.currentThread().isInterrupted()的值改为true,但是线程并没有被中断而且也没有输出Interrupted run,只是报了一个异常。那是因为InterruptedException将线程进行了复位(将true又变回了false);这样做的目的就是为了将线程的中断权限交给线程自己,由它自己来决定线程是否中断。因为main线程是不知道异步线程的运行的状态。将中断权限交给线程自己去处理,这样就达到了友好中断的目的
当线程复位后我们一定要在catch中去做处理,否则我们的中断就没有任何意义了

总结:

调用Interrupt都做了什么:

  1. 发出中断指令;将Thread.currentThread().isInterrupted()的值变为true
  2. 调用unpark方法唤醒线程并抛出异常
    以上两个操作都是在JVM层面去处理的
    线程的复位还有另外一种方式Thread.interruped()

在这里有关于此问题不懂的朋友欢迎给我留言
建议先看一下下边两个文章会更好的理解
java线程的声明周期
java创建线程的三种方式

你可能感兴趣的:(Java并发编程,java)