在java程序中,当使用Thread.sleep()或者BlockingQueue.take()等阻塞方法时,需要处理InterruptedException。对于这种异常,通常有2种方案进行处理。
1. 传递异常:将异常传递给方法的调用者。示例如下:
BlockingQueue<String> queue; public String getNextString() throws InterruptedException{ return queue.take(); }
2. 恢复异常:在大多数情况下,异常是可以传递的,但有些情况是无法传递异常的,比如在Runnable的run()方法中,我们不可以抛出异常。此时需要我们恢复异常,这样在调用栈中更高层次的代码将看到引发了一个中断。示例如下:
BlockingQueue<String> queue; public String getNextString() throws InterruptedException{ String result=null; try{ result=queue.take(); }catch(InterruptedException e){ Thead.currentThread.interrupt(); }finally{ Return result; } }
以上是处理InterruptedException的常用方法,对于InterruptedException,千万不要捕获异常但不做任何处理。 当我们使用中断来结束线程时,在catch块中也可以使用interrupted()来清除异常。
任务取消
多线程编程时,有时候需要取消某些任务线程,有以下3种方案:
1. 设定一个线程取消的标记,任务线程定期的检查这个标记。示例如下:
class Task implements Runnable{ private volatile boolean cancel=false; @Override public void run() { while(!cancel){ System.out.println("..."); } } public void cancel(){ cancel=true; } }2. 上面的示例描述的是最一般的场景,试想一下,如果while(!cancel)循环中调用了一个阻塞的方法,那么有这样一种可能:程序可能阻塞在某个方法中。示例如下:
class Task implements Runnable{ private volatile boolean cancel=false; private BlockingQueue<String> blockingQueue; public Task(BlockingQueue<String> queue){ this.blockingQueue=queue; } @Override public void run() { try{ while(!cancel){ System.out.println("..."); this.blockingQueue.take();//当程序阻塞在此处时,即便cancel被更新了,也无法感知,这种情况下,程序永远无法退出。 } }catch(InterruptedException e){ Thread.currentThread().interrupt(); } } public void cancel(){ cancel=true; } }
当while(!cancel)循环中调用了一个阻塞的方法时,使用标记位的方式终止程序就不再使用了,此时使用中断的方式退出程序:
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class CDemo { public static void main(String [] args) throws Exception{ BlockingQueue<String> b=new ArrayBlockingQueue<String>(3); b.put("a"); b.put("ab"); b.put("abc"); Task task=new Task(b); task.start(); Thread.sleep(4000); task.cancel(); } } class Task extends Thread{ private BlockingQueue<String> blockingQueue; public Task(BlockingQueue<String> queue){ this.blockingQueue=queue; } @Override public void run() { try{ while(true){ // if(Thread.currentThread().isInterrupted()) //一定注意,这行是错误做法 if(interrupted())//判断当前线程是否被中断 break; String str=this.blockingQueue.take(); System.out.println(str); } }catch(InterruptedException e){ //Thread.currentThread().interrupt(); interrupted();//清除中断痕迹 }finally{ System.out.println("线程结束!"); } } public void cancel(){ this.interrupt();//中断当前线程 // Thread.currentThread().interrupt(); //一定注意,这行是错误做法 } }
3. 在生产者消费者问题中,使用“毒丸对象”来终止消费者线程。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; public class Coordinator { public static final Object POISON_PILL = new Object();//special object to kill consumers private int productCount = 1; private int consumerCount = 3; public void startAll() throws Exception{ BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(5); CountDownLatch activeProductorNum = new CountDownLatch(productCount); CountDownLatch activeConsumerNum = new CountDownLatch(consumerCount); for(int i = 0; i < consumerCount; i++){ new Thread(new Consumer("consumer " + i, queue,activeConsumerNum)).start(); } for(int i = 0; i < productCount; i++){ new Thread(new Producer("producer " + i, queue, activeProductorNum)).start(); } activeProductorNum.await();//等待所有生产者生产结束 System.out.println("All producer finished, putting POISON_PILL to the queue to stop consumers!"); queue.put(POISON_PILL); activeConsumerNum.await();//等待所有生产者生产结束 System.out.println("All consumer finished!"); } public static void main(String[] args) throws Exception{ new Coordinator().startAll(); } } class Producer implements Runnable { private String name; private BlockingQueue<Object> queue; private CountDownLatch activeProducerNum; public Producer(String name, BlockingQueue<Object> queue, CountDownLatch activeProducerNum){ this.name = name; this.queue = queue; this.activeProducerNum=activeProducerNum; } @Override public void run() { try { for(int i=0;i<10;i++){ queue.put(i); System.out.println(name + " produced "+i); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally{ System.out.println(name + " finished."); activeProducerNum.countDown(); } } } class Consumer implements Runnable { private String name; private BlockingQueue<Object> queue; private CountDownLatch activeConsumerNum; public Consumer(String name, BlockingQueue<Object> queue,CountDownLatch activeConsumerNum){ this.name = name; this.queue = queue; this.activeConsumerNum=activeConsumerNum; } @Override public void run() { try { while (true) { Object item = queue.take(); if (item == Coordinator.POISON_PILL) { queue.put(item);//放回继续毒害其他消费者 break; } System.out.println(name + " consumed "+item); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally{ System.out.println(name + " finished"); activeConsumerNum.countDown(); } } }