中断是一种协作机制,通过这种机制可以要求指定线程在可以暂停的地方停止当前任务,但这个要求可以无视,我们也经常这么做(虽然不好),那应该这么对待其它线程发来的中断要求呢?
在上一篇如何优雅地取消线程任务 中提到了通过中断可以取消线程正在进行的任务,现在针对中断这件事情再来简单聊聊。
阻塞库如何抛出中断 JAVA中有很多带阻塞方法的工具类,这种方法往往会声明一个受检查的异常InterruptedException,如果被中断,它会尝试提前结束阻塞状态,并抛给调用者一个InterruptedException异常,让对方决定何去何从。
用ArrayBlockingQueue.offer(E, long, TimeUnit)为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class ArrayBlockingQueue <E > extends AbstractQueue <E > implements BlockingQueue <E >, java .io .Serializable { public boolean offer (E e, long timeout, TimeUnit unit) throws InterruptedException { checkNotNull(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this .lock; lock.lockInterruptibly(); try { while (count == items.length) { if (nanos <= 0 ) return false ; nanos = notFull.awaitNanos(nanos); } enqueue(e); return true ; } finally { lock.unlock(); } } }
传递中断 如果捕获到一个中断异常不知道怎么处理它,那么可以考虑把这个烫手山芋扔出去,扔给你的上级(调用者),即传递中断。
传递方式1: 不捕获中断异常 只要在方法上添加一个InterruptedException的声明,就能轻松把这个锅甩给调用者,因为此时你也成为了可中断大军的一员。既然解决不了,那就加入。
1 2 3 4 5 6 7 8 9 10 import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class Main { public static void main (String[] args) throws InterruptedException { ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(100 ); queue.offer(new Object(), 1L , TimeUnit.MINUTES); } }
传递方式2: 捕获再抛出 如果希望发生中断时自己可以做点扫尾操作,那么可以捕获中断异常,做点小动作后再抛出这个异常(你也可以抛出其它自定义异常)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class Main { public static void main (String[] args) throws InterruptedException { ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(100 ); try { queue.offer(new Object(), 1L , TimeUnit.MINUTES); } catch (InterruptedException e) { System.out.println("有人想中断我,我从了" ); throw e; } } }
恢复中断 当我们捕获到中断异常的时候,如果再去调用Thread.isInterrupted()往往得到的是false,因为这件事只有一个人来处理就够了,所以抛出异常后会清除中断状态,比如Thread,sleep()。
1 2 3 4 5 6 7 8 9 public class Thread implements Runnable { public static native void sleep (long millis) throws InterruptedException ;
因此,线程不方便抛出异常的时候(比如在实现Runnable,我们知道run()方法没有声明异常),我们可以捕获到中断异常后再次把线程状态置为中断。这件事我管不了, 谁爱管谁管。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Main { public static void main (String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run () { while (true ) { try { System.out.println("可有人想中断我?" + isInterrupted()); sleep(1000 ); } catch (InterruptedException e) { System.out.println("有人想中断我,我拒绝" ); System.out.println(isInterrupted()); interrupt(); } } } }; thread.start(); Thread.sleep(3000 ); thread.interrupt(); } }
总结
线程处于中断状态表明有人想让它赶紧结束,但得到这个信号的线程可以做出自己的选择;
不要捕获到它却冷漠的不做任何响应(可以不爱,莫要伤害)。