在Java语言的开发工作中,我们经常会碰到这样一类异常--InterruptedException(中断异常)。在绝大多数时候,我们的处理方式无非是catch注它,然后再输出异常信息,更或者是干脆直接忽略它了。那么这是否是一种正确的处理方式呢,要想搞清楚这件事,我们又必须要了解什么是InterruptedException,什么情况下会导致此异常的发生呢?本文笔者来简单讲述讲述这方面的内容,了解中断中断异常方面的知识将有助于我们在分布式的程序中处理这样的异常。
现在一个首要的问题来了,什么是中断异常,InterruptedException到底意味着什么意思呢?下面是笔者通过阅读IBM官网上面对于此的定义:
When a method throws InterruptedException, it is telling you several things in addition to the fact that it can throw a particular checked exception. It is telling you that it is a blocking method and that it will make an attempt to unblock and return early
大致意思如下:InterruptedException实质上是一个检测异常,它表明又一个阻塞的被中断了,它尝试进行解除阻塞操作并返回地更早一些。中断阻塞方法的操作线程并不是自身线程干的,而是其它线程。而中断操作发生之后,随后会抛出一个InterruptedException,伴随着这个异常抛出的同时,当前线程的中断状态重新被置为false。这时,我们谈到了线程的中断状态,可能有些读者会有点晕了,下面我们来理理这段关系。
1.public static boolean interrupted(); // 检测当前线程是否已经中断,此方法会清除中断状态,也就是说,假设当前线程中断状态为true,第一次调此方法,将返回true,表明的确已经中断了,但是第二次调用后,将会返回true,因为第一次调用的操作已将中断状态重新置为false了。
2.public boolean isInterrupted() ; // 检测当前线程是否已经中断,此方法与上一方法的区别在于此方法不会清除中断状态。
3.public void interrupt(); //将线程中断状态设置为true,表明此线程目前是中断状态。此时如果调用isInterrupted方法,将会得到true的结果。
通过上述方法的解释,我们可以得出这样的一个结论:interrupt方法本质上不会进行线程的终止操作的,它不过是改变了线程的中断状态。而改变了此状态带来的影响是,部分可中断的线程方法(比如Object.wait, Thread.sleep)会定期执行isInterrupted方法,检测到此变化,随后会停止阻塞并抛出InterruptedException异常。但这是否意味着随后线程的退出呢?不是的,
下面是笔者对于此3个方法进行的一个简单测试demo。代码如下:
public class InterruptedException { public static void main(String[] args) throws Exception { System.out.println("初始中断状态:" + Thread.currentThread().isInterrupted()); Thread.currentThread().interrupt(); System.out.println("执行完interrupt方法后,中断状态:" + Thread.currentThread().isInterrupted()); System.out.println("首次调用interrupted方法返回结果:" + Thread.currentThread().interrupted()); System.out.println("此时中断状态:" + Thread.currentThread().isInterrupted()); System.out.println("第二次调用interrupted方法返回结果:" + Thread.currentThread().interrupted()); System.out.println("此时中断状态:" + Thread.currentThread().isInterrupted()); } }输出结果如下:
初始中断状态:false 执行完interrupt方法后,中断状态:true 首次调用interrupted方法返回结果:true 此时中断状态:false 第二次调用interrupted方法返回结果:false 此时中断状态:falseInterruptedException异常的抛出并不是意味着线程必须得终止,它只是提醒当前线程有中断操作发生了,接下来怎么处理完全取决于线程本身,一般有3种处理方式:
public class TaskQueue { private static final int MAX_TASKS = 1000; private BlockingQueue<Task> queue = new LinkedBlockingQueue<Task>(MAX_TASKS); public void putTask(Task r) throws InterruptedException { queue.put(r); } public Task getTask() throws InterruptedException { return queue.take(); } }
public class PlayerMatcher { private PlayerSource players; public PlayerMatcher(PlayerSource players) { this.players = players; } public void matchPlayers() throws InterruptedException { Player playerOne, playerTwo; try { while (true) { playerOne = playerTwo = null; // Wait for two players to arrive and start a new game playerOne = players.waitForPlayer(); // could throw IE playerTwo = players.waitForPlayer(); // could throw IE startNewGame(playerOne, playerTwo); } } catch (InterruptedException e) { // Just propagate the exception throw e; } } }
public class PlayerMatcher { ... public void matchPlayers() throws InterruptedException { Player playerOne, playerTwo; try { while (true) { playerOne = playerTwo = null; // Wait for two players to arrive and start a new game playerOne = players.waitForPlayer(); // could throw IE playerTwo = players.waitForPlayer(); // could throw IE startNewGame(playerOne, playerTwo); } } catch (InterruptedException e) { // If we got one player and were interrupted, put that player back if (playerOne != null) players.addFirst(playerOne); // Then propagate the exception throw e; } } }
public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
public Task getNextTask(BlockingQueue<Task> queue) { boolean interrupted = false; try { while (true) { try { return queue.take(); } catch (InterruptedException e) { interrupted = true; // fall through and retry } } } finally { if (interrupted) Thread.currentThread().interrupt(); } }
public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException swallowed) { /* DON'T DO THIS - RESTORE THE INTERRUPTED STATUS INSTEAD */ } } }