线程中断

本文转载https://my.oschina.net/ljhlgj/blog/1572376

中断线程

thread.interrupt()用来中断线程,即将线程的中断状态位设置为true,注意中断操作并不会终止线程,不像stop()会立即终止一个运行中的线程,中断仅仅是将线程中断位设置为true(默认false)。线程会不断的检查中断位,如果线程处于阻塞状态(sleep、join、wait)且中断,就会抛出InterreptException来唤醒线程,交由应用程序处理;如果线程未阻塞且中断,也要交由应用程序处理;是终止线程,还是继续执行需要根据实际情况做出合理的响应。

如何响应线程中断

thread.interrupt()后,线程的中断位设置为true,没有任何语言方面的需求一个被中断的线程应该终止。中断只是为了引起该线程的注意,来决定如何应对中断。有些很重要的线程,以至于他们不理会中断,而是继续执行,但更多的情况下,线程应该把中断看做是一个终止请求,在终止线程前,有时间做一些收尾清理工作,如下:

public void run() {
        try {
         	/*
             * 不论while中是否调用过线程阻塞的方法,如sleep、join、wait,这里还是需要加上
             * !Thread.currentThread().isInterrupted()条件来判断线程是否终止。
             * 原因:即使调用了阻塞方法但线程可能仍没有阻塞,这样会更安全、更及时。
             */
            while (!Thread.currentThread().isInterrupted()) {
                //do more work
            }
        } catch (InterruptedException e) {
            //线程在阻塞期间被中断了
        } finally {
            //线程结束前做一些清理工作
        }
}

注意:这里使用Thread.currentThread().isInterrupted(),而不是**Thread.isInterrupted()**来判断线程是否中断,因为Thread.isInterrupted()判断线程中断位为true后,会将中断位设置为false,而Thread.currentThread().isInterrupted()不会将中端位在设置为false。线程阻塞中断是抛出异常后,同样会将中断位设置为false。

public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                ...
                sleep(delay);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();//重新设置中断位,否则线程不能退出
            }
        }
}

注意:synchornized、reentrantLock.lock()获取锁操作造成的阻塞在中断状态下不能抛出InterruptException,即获取锁操作是不能被中断的,要一直阻塞等到到它获取到锁为止。也就是说如果产生了死锁,则不能被中断。可以使用超时锁来打破死锁,reentrantLock.tryLock(timeout,unit)在timeout后会抛出InterruptException,唤醒线程做响应操作

在捕获到InterruptException后如何处理呢,通常有如下两种方式,这两种方式本质上是一样的,都是将中断交由用户应用程序来处理。

  • 1.在catch子句中调用Thread.currentThread.interrupt()来重设中断状态位(因为抛出中断异常后,中断状态位会被清除,即false),让外界判断Thread.currentThread.isInterrupted()来决定线程的行为。
  try {
        sleep(delay);
    } catch (InterruptedException e) {
        Thread.currentThread().isInterrupted();
    }
    1. 更加直接,不用捕获异常,直接抛出InterruptedException,让用户程序去处理异常。
  try {
        sleep(delay);
    } catch (InterruptedException e) {
        throw e;
    }

终止线程

让一个运行中的线程终止,通常有两种方法:1.使用信号量;2.使用线程中断。

  • 1.使用信号量终止线程:设置一个变量(信号量),通过更改变量的状态,来决定线程是否要终止,该方式不能终止阻塞的线程。
class Scheduler{
   volatile boolean stop = false;// 线程中断信号量
   private Worker worker = new Worker("my task worker");
   void start(){
    	worker.start();
        stop=false;
    }
    void stop(){
    	stop=true;
    }

    class Worker extend Thread{
        public void run() {
            while (!stop) {
                long time = System.currentTimeMillis();
                /*
                 * 用while循环模拟sleep(),这里不要用sleep,否则在线程阻塞时可能会抛
                 * InterruptedException而退出,这样while检测stop条件就不会执行,失去了意义。
				 *
				 * 不论是模拟sleep,还是使用sleep,都建议线程任务中做这样的操作,以控制线程执行速率
                 */
                while ((System.currentTimeMillis() - time < 1000)) {
                	//do something
                }
            }
        }
    }
}

注意:信号量一定要是多线程可见的,这里使用volatile关键字,也可以使用Automicxxx

  • 2.使用线程中断来终止线程:用thread.interrupt()设置线程中断位为true,让应用来决定线程是否要终止,该方式能让阻塞的线程抛出InterruptException来唤醒线程,从而决定线程的行为。
class Scheduler{
   private Worker worker = new Worker("my task worker");
   void start(){
    	worker.start();
    }
    void stop(){
    	worker.interrupt();//设置线程中断位为true
    }

    class Worker extend Thread{
        public void run() {
            while (!Thread.currentThread.isInterrupted()) {
                try{
                	// do something
                	Thread.sleep(10);
                }catch(InterruptException e){
                	// 重新设置线程状态位
                    Thread.currentThread.interrupted();
                }
            }
        }
    }
}

注意:这里也可以使用信号量代替Thread.currentThread.isInterrupted(),在捕获到异常后,设置信号量即可,但不太简便。

I/O阻塞中断

i/o操作可以阻塞线程很长时间,特别是牵扯到网络socket时,那么在i/o阻塞时,线程中断会有什么现象呢?有两种情况:

  • 1.线程在可中断通道(实现InterruptibleChannel接口)上,调用io阻塞操作(serverSocketChannel.accept(),socketChannel.connect(),socketChannel.open(),socketChannel.read(),socketChannel.write()等)而阻塞线程后,又被设置为中断时(调用thread.interrupt()),通道将被关闭,并在io阻塞处会抛出ClosedByInterruptException,这样应用程序就能响应改中断异常,做出合适的处理。
  • 2.线程在不可中断通道(java1.0前的传统io模型)上,调用io阻塞操作,即使线程被设置为中断,也不会唤醒阻塞线程,因为这样的io阻塞在线程中断后不会抛出任何异常。幸运的是java平台提供了一种解决方案:调用阻塞改线程的socket的close方法,此时在socket.accept()处就抛出SocketException,这与thread.interrupt() 引起InterruptException抛出很相似,可以响应SocketException来终止并退出线程。

总结

  • 1.中断一个线程,只是为了引起该线程的注意(唤醒阻塞),让其自己决定如何应对,而不是像stop一样直接终止掉线程。
  • 2.阻塞的线程如果被中断,会在阻塞处抛出InterruotException来唤醒线程,并将线程中断标志位设置为false,因为线程已经处理了异常(抛出),就要回到就绪状态。
  • 3.synchronized,Lock.lock获取锁操作不能被中断,如果拿不到资源,会一直阻塞下去,可以使用可中断的加锁操作Lock.lockInterruptibly(),也可以使用超时锁Lock.tryLock(timeout,unit),在超时后会抛出异常。
  • 4.中断其实是线程的一种协作机制,比直接结束线程操作弱一点,给程序一些时间来决定如何处理,如sleep方法对中断的处理是抛出中断异常,并重置中断位置。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值