zoukankan      html  css  js  c++  java
  • Java线程中断机制-如何中断线程

    介绍:

    对于线程一共分为五个状态:新建状态,就绪状态,阻塞状态,运行状态,死亡状态,有时候把阻塞状态又分为同步阻塞和等待阻塞。


    有时想让主线程启动的一个子线程结束运行,我们就需要让这个子线程中断,不再继续执行。线程是有中断机制的,我们可以对每个线程进行中断标记,注意只是标记,中断与否还是虚拟机自己的事情,虚拟机自己家的事情,我们也就说说,不能实际操作控制他家。java中的Thread类是一个对线程进行操作的类,提供了中断线程的方法interrupt(),在API中是这么定义的(中文的翻译可能不准确)。

    在Thread中其实还有一个stop()方法也是中断线程的方法,但是这个方法太粗鲁了,人家好好的线程正在运行,只要调用了stop()方法,线程就会中断,不管是在进行什么操作。这个方法已经被废弃,他的执行会带来一些不可确定的状况。


    线程可以调用中断自己的方法interrupt()方法,但是中断也是有条件的,在线程调用interrupt()方法的时候虚拟机会在此线程上标记一个标志(这个中断标志只是一个布尔类型的变量),代表这个线程可能被中断,在后面的中断操作也是根据这个中断标志执行的。可以说interrupt()方法是一种友好的方法,总是和虚拟机商量着来。如果一个线程处于了阻塞状态(如线程调用了sleep()、join()、wait()、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法(sleep()、join()、wait()及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

    线程的一些状态都可能影响到这个中断标记从而结束中断。下面这个例子验证中断标志被除掉的说法

    package demo_thread;
    
    public class Interrupt {
        public static void main(String[] args) {
            A a = new A();
            Thread t1 = new Thread(a);
            t1.start();
            t1.interrupt();
            System.out.println("执行睡眠之前1:"+t1.isInterrupted());
            try {
                System.out.println("执行睡眠之前2:"+t1.isInterrupted());
                t1.sleep(1000);//线程进入阻塞状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println("执行睡眠之后:"+t1.isInterrupted());
            }
        }
    }
    class A implements Runnable{
    
        @Override
        public void run() {
                System.out.println(Thread.currentThread());
        }
    }

    在main方法中创建一个线程,线程中重写的run()方法输出线程号,只是一个显示分隔,具体看在执行让线程阻塞方法之前和之后线程的中断标记。isInterrupted()方法是判断线程是否执行中断的方法,就是判断中断标记是否存在的方法。

    输出:

    执行睡眠之前1:true
    执行睡眠之前2:true
    Thread[Thread-0,5,main]
    执行睡眠之后:false
    可以看到只要执行了sleep()方法,线程的中断标记就会被清除。分别调用执行了join()、wait()方法,可以看到他们的共同点都是需要捕获一个InterruptedException,在调用join()方法的时候得到了和sleep()方法一样的输出结果,但是在调用wait()方法的时候,虽然在后面调用notify()方法,但是是否获得执行权限还是无法预料的。

    中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断,中断或者不中断。某些线程非常重要,以至于它们应该不理会中断,而是在处理完抛出的异常之后继续执行,但是更普遍的情况是,一个线程将把中断看作一个终止请求。

    两个很像的方法的区别:

    interrupted():测试当前线程是否中断。 该方法可以清除线程的中断状态 。换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false(除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经检查过)。

    isInterrupted():测试这个线程是否被中断。线程的中断状态不受此方法的影响。

    如何中断线程:


    接下来看请求中断之后发生的事情:在网上看到一个例子

    package demo_thread;
    
    public class Interrupt {
            public static void main(String[] args) {
                try {
                    MyThread thread = new MyThread();
                    thread.start();
                    Thread.sleep(2000);
                    thread.interrupt();//请求中断MyThread线程
                } catch (Exception e) {
                    System.out.println("main catch");
                    e.printStackTrace();
                }
                System.out.println("end!");
            }
    }
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                if (this.interrupted()) {
                    System.out.println("should be stopped and exit");
                    break;
                }
                System.out.println("i=" + (i + 1));
            }
            //尽管线程被中断,但并没有结束运行。这行代码还是会被执行
            System.out.println("this line is also executed. thread does not stopped");
        }
    }
    
    


    在文章的开头处已经说了,线程执行中断方法,只是给线程一个中断标记,是否中断是他自己的事

    其中一次的输出:

    ……
    i=75722
    end!
    should be stopped and exit
    this line is also executed. thread does not stopped

    由于线程执行了sleep()方法,进入阻塞状态,所以在从阻塞状态从新回到运行状态的时候,发现自己已经有了中断标记,执行if中的语句,线程的睡眠和执行总是和计算机内部有关系,所以每次输出的结束数字总是不一样的。

    通过输出结果我们发现一个很重要的问题,我们要的是线程的中断,也就是关于线程的方法需要停止执行,但是在循环外面的输出语句也执行了,这个中断只是中断了循环的执行。


    那么我们应该怎么中断线程呢?利用线程中断异常就是一个很好的办法,既然是异常,通常异常可以中断线程的运行,使程序挂掉,我们自己抛出一个异常让线程中断

    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0; i < 500000; i++) {
                    if (this.interrupted()) {
                        System.out.println("should be stopped and exit");
                        throw new InterruptedException();
                    }
                    System.out.println("i=" + (i + 1));
                }
                System.out.println("this line cannot be executed. cause thread throws exception");//这行语句不会被执行!!!
            } catch (InterruptedException e) {
                System.out.println("catch interrupted exception");
                e.printStackTrace();
            }
        }
    }


    输出:

    ……
    i=131892
    should be stopped and exit
    end!

    但是通过抛出异常也带来了问题,我们不能通过抛出异常来处理问题,还需要在捕获异常的代码中加上处理的语句,例如保存用户的操作等等,让这次中断看起来是很正常的中断。
    我们还可以通过让程序主动抛出异常,在线程阻塞的时候,执行中断操作,抛出异常,在循环中循环打印当前时间,

    package demo_thread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class SleepInterruptTest {
        public static void main(String[] args) {
            SleepThread t1 = new SleepThread();
            t1.start();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }
            t1.interrupt();//主动打断线程,使SleepThread线程抛出异常
        }
    
    
    
    }
    class SleepThread extends Thread{
        public void run(){
            while(true){
                try{
                    SimpleDateFormat sim = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
                    System.out.println(sim.format(new Date()));
                    sleep(1000);
                }
                catch(InterruptedException e){
                    System.out.println("线程中断");
                    return;
                }
            }
        }
    }


    输出:

    2017年10月14日 10:40:01
    2017年10月14日 10:40:02
    2017年10月14日 10:40:03
    2017年10月14日 10:40:04
    2017年10月14日 10:40:05
    线程中断

    参考:http://www.cnblogs.com/hapjin/p/5450779.html

  • 相关阅读:
    oracle
    SQL Server- 行列转换 行转列,多行转多列
    oracle
    asp.net 去掉小数点后面多余的0,本身为0则不显示
    asp.net 去掉小数点后面多余的0,本身为0还是显示为0
    asp.net 经常用到需要判断文本框是否输入的数字是小数,有无正负,几位小数,可以封装一起判断
    ASP.NET gridview导出excel,防止繁体产生有乱码的方式
    SQL- @@ROWCOUNT -返回上一行执行影响的行行数
    mui-a标签跳转
    浏览器地址截取
  • 原文地址:https://www.cnblogs.com/duzhentong/p/7816546.html
Copyright © 2011-2022 走看看