3 线程异常
线程在执行其run方法时,很有可能抛出异常。而run方法签名中,并未声明会抛出任何检查型异常。但在实际程序中,run方法中极其可能抛出一个异常,从而导致此线程被终止。更糟糕的是,如果线程因为异常终止,我们无法在主线程中使用try...catch...进行异常的捕获,从而可能导致一些问题的发生,例如无法释放某些资源等。主线程之所以不处理子线程抛出的RuntimeException,是因为线程是异步的,子线程没结束,主线程可能已经结束了。Thread类中的setUncaughtExceptionHandler就是处理线程中那些未捕获的异常,更明确的说,它处理那些未捕获的运行时异常。以下是一个例子,:
public class Dummy {
public static void main(String[] args) {
ThreadA threadA = null;
ThreadB threadB = null;
try {
UncaughtException exe = new UncaughtException();
threadA = new ThreadA();
threadA.setName("threadA");
threadA.setUncaughtExceptionHandler(exe);
threadA.start();
//threadA.run(); // exception thrown by common method can by caught in main thread
} catch (Exception e) {
System.out.println("catch RuntimeException e " + e.getMessage());
}
// main thread can not caught the exception thrown in ThreadB
try {
threadB = new ThreadB();
threadB.start();
} catch (Exception e) {
System.out.println("catch RuntimeException e " + e.getMessage()); // not effect
}
}
}
class UncaughtException implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("thread " + t.getName() + " throws a uncaught excpetion " + e.getMessage());
e.printStackTrace();
}
}
class ThreadA extends Thread{
public void run(){
double a = 12 / 0;
}
}
class ThreadB extends Thread{
public void run(){
try {
double a = 12 / 0 ;
} catch (ArithmeticException e) {
throw e;
}
}
}
程序的输出结果:
thread threadA throws a uncaught excpetion / by zero
java.lang.ArithmeticException: / by zero
at org.java.test1.ThreadA.run(Dummy.java:40)
Exception in thread "Thread-1" java.lang.ArithmeticException: / by zero
at org.java.test1.ThreadB.run(Dummy.java:48)
线程内部的处理机制是这样子的:当一个线程突然间被一个无法捕获的异常终止时,首先这个线程本身会处理这个异常,由jvm调用dispatchUncaughtException方法,查看线程是否设置了异常处理方法,如果没有为此线程设置异常处理方法,此时线程会查看此线程所在线程组是否设置了线程异常处理方法,将异常处理交给线程组,然后ThreadGroup的uncaughtException方法会处理这个异常(也由jvm调用),在这个方法里,ThreadGroup会首先判断这个线程组是否还有父线程组,如果有父线程组,则继续交由父线程组处理这个异常,如果不存在父线程组,其会调用这个线程的默认异常处理方法,如果这个线程有默认的异常处理策略,则用这个默认的异常处理策略进行异常的处理,否则的话,会判断这个异常是否属于ThreadDeath类型(extends Error),如果这个异常属于ThreadDeath类型,则放弃处理,否则打印这个异常信息。
同样,我们可以设置线程的默认异常处理策略,通过setDefaultUncaughtExceptionHandler即可,我们也可以通过调用getDefaultUncaughtExceptionHandler和getUncaughtExceptionHandler得到线程默认的异常处理程序信息和线程异常处理程序信息
3.1 InterruptedException异常
Thread.sleep()、 Thread.join() 或 Object.wait()都可以抛出InterruptedException,它是一个检查异常(checked exception)。当一个线程在wait set中等待或者调用了sleep方法,而另一个线程调用了interrupt方法中断当前线程时,就会抛出InterruptedException。
当一个方法抛出InterruptedException,表示这个方法是一个阻塞方法。
3.2 阻塞方法
阻塞的方法,不同于一般的普通方法。一般方法的完成只取决于它所要做的事情,以及是否有足够多可用的计算资源。而阻塞的方法还要取决于外部的一些事件,例如I/O完成,等待另一个线程释放对象锁等。一般方法在他们的工作做完后即可结束,而阻塞方法却不一定,其结束很难预测,因为他们还受外部因素的影响。
阻塞方法可能因为等不到外部的事件而无法结束,那么让阻塞方法可取消就非常有用。可取消是指能从外部使之在正常结束工作前终止的操作。由Thread 提供并受 Thread.sleep()和Object.wait()支持的中断机制就是一种取消机制;它允许一个线程请求另一个线程停止它正在做的事情。当一个方法抛出 InterruptedException时,它是在告诉您,如果执行该方法的线程被中断,它将尝试停止它正在做的事情而提前返回,并通过抛出InterruptedException表明它提前返回。
3.3 处理InterruptedException异常
如果一个方法抛出InterruptedException,表示这个方法为阻塞的方法,那么调用这个阻塞方法的方法也是一个阻塞方法。所以,我们必须有策略来处理InterruptedException异常。
3.3.1 将InterruptedException异常传递给调用者
public class BoundQueue {
//
private static final int MAX_COUNT = 1000;
private BlockingQueue<Message> queue = new LinkedBlockingQueue<Message>(MAX_COUNT);
//
public void put(Message msg) throws InterruptedException{
queue.put(msg);
}
public Message take() throws InterruptedException{
return queue.take();
}
}
3.3.2 在重新抛出InterruptedException异常前作特定的工作
有时候,我们必须在抛出InterruptedException异常前,做一些特定的工作,例如:当一个游戏需要两个人同时加入才可以开始,如果一个人到来,程序在等待第二个人到来前中断,此时我们需要将第一个人放回到队列中,然后抛出InterruptedException异常警告,才不会让第一个的请求丢失。
public class MatcherPlayer {
//
private final PlayerQueue queue = new PlayerQueue(2);
private PlayerSource source;
private Game game;
private Player one;
private Player two;
public MatcherPlayer(PlayerSource source){
this.source = source;
}
public void matchPlayer() throws InterruptedException {
try {
while(true){
one = source.waitForOne();
two = source.waitForOne();
game.start(one, two);
}
} catch (InterruptedException e) {
if(one != null){
queue.put(one);
}
throw e;
}
}
}
3.3.3 捕捉 InterruptedException 后恢复中断状态
有时候抛出 InterruptedException 并不合适,例如当由 Runnable 定义的任务调用一个可中断的方法时,就是如此。
public class TaskRunner implements Runnable {
//
private BlockingQueue<Message> queue;
public TaskRunner(BlockingQueue<Message> queue){
this.queue = queue;
}
public void run(){
try {
while(true){
Message message = queue.take();
message.execute();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
3.3.4 生吞中断
处理InterruptedException时采取的最糟糕的做法是生吞它 —— 捕捉它,然后既不重新抛出它,也不重新断言线程的中断状态。对于不知如何处理的异常,最标准的处理方法是捕捉它,然后记录下它,但是这种方法仍然无异于生吞中断,因为调用栈中更高层的代码还是无法获得关于该异常的信息。
public class TaskRunnerBadly implements Runnable {
//
private BlockingQueue<Message> queue;
public TaskRunnerBadly(BlockingQueue<Message> queue){
this.queue = queue;
}
public void run(){
try {
while(true){
Message message = queue.take();
message.execute();
}
} catch (InterruptedException e) {
// NOP
}
}
}
3.4 不可中断的阻塞方法
并非所有的阻塞方法都抛出 InterruptedException。输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会提前返回。然而,对于套接字 I/O,如果一个线程关闭套接字,则那个套接字上的阻塞 I/O 操作将提前结束,并抛出一个 SocketException。java.nio 中的非阻塞 I/O 类也不支持可中断 I/O,但是同样可以通过关闭通道或者请求 Selector 上的唤醒来取消阻塞操作。类似地,尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 ReentrantLock 支持可中断的获取模式。
分享到:
相关推荐
Concurrent Programming in Java Design Principles and Pattern英文版 2.48M Java并发编程设计原则与模式_第二版(原书中文版) 19.4M Concurrent_Programming_in_Java_Design_Principles_Lecture DougLea
详细的java 多线程相关知识 并附有相关练习题
Concurrent Programming in Java - Design Principles and Patterns
java concurrent 多线程 PPT
concurrent programming in java design principles and patterns .chm
Doug Lea, Concurrent Programming in Java Design Principles and Patterns
详细介绍java多线程编程的各个基础概念。JUC作者doug lea著
在并发或多线程应用程序中使用Java编程语言的简介。
word版本的资料,网上...Concurrent Programming in Java™: Design Principles and Patterns, Second Edition Doug Lea Publisher: Addison Wesley Second Edition October 01, 1999 ISBN: 0-201-31009-0, 432 pages
Doug Lea Java Concurrent Programming
This book shows readers how to use the Java platform's threading model more precisely by helping them to understand the patterns and tradeoffs associated with concurrent programming
Concurrent - Programming in Java.pdf,ppt,Doug Lea
Concurrent Programming on Windows 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系...
Title: Learning Concurrent Programming in Scala, 2nd Edition Author: Aleksandar Prokopec Length: 382 pages Edition: 2nd Revised edition Language: English Publisher: Packt Publishing - ebooks Account ...
Concurrent Programming in Java™: Design Principles and Patterns, Second Edition. 介绍并发编程的好的著作,著名的并发大师 Doug Lea的杰作。
Java并发编程-设计原则与模式(Concurrent.Programming.in.Java-Design.Principles.and.Patterns(Second.Edition))(中英版)
Concurrent Programming in Java Thread 看看吧
资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 封底 Java多线程无处不在,...