`
frank-liu
  • 浏览: 1680881 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java thread中对异常的处理策略

阅读更多

前言

    想讨论这个话题有一段时间了。记得几年前的时候去面试,有人就问过我一个类似的问题。就是java thread中对于异常的处理情况。由于java thread本身牵涉到并发、锁等相关的问题已经够复杂了。再加上异常处理这些东西,使得它更加特殊。 概括起来,不外乎是三个主要的问题。1. 在java启动的线程里可以抛出异常吗? 2. 在启动的线程里可以捕捉异常吗? 3. 如果可以捕捉异常,对于checked exception和unchecked exception,他们分别有什么的处理方式呢?

     现在, 我们就一个个的来讨论。

线程里抛出异常

    我们可以尝试一下在线程里抛异常。按照我们的理解,假定我们要在某个方法里抛异常,需要在该定义的方法头也加上声明。那么一个最简单的方式可能如下:

public class Task implements Runnable {

	@Override
	public void run() throws Exception {
		int number0 = Integer.parseInt("1");
		throw new Exception("Just for test");
	}
}

    可是,如果我们去编译上面这段代码,会发现根本就编译不过去的。系统报的错误是:

Task.java:3: error: run() in Task cannot implement run() in Runnable
    public void run() throws Exception {
                ^
  overridden method does not throw Exception
1 error

     由此我们发现这种方式行不通。也就是说,在线程里直接抛异常是不行的。可是,这又会引出一个问题,如果我们在线程代码里头确实是产生了异常,那该怎么办呢?比如说,我们通过一个线程访问一些文件或者对网络进行IO操作,结果产生了异常。或者说访问某些资源的时候系统崩溃了。这样的场景是确实可能会发生的,我们就需要针对这些情况进行进一步的讨论。

异常处理的几种方式

     在前面提到的几种在线程访问资源产生了异常的情况。我们可以看,比如说我们访问文件系统的时候,会抛出IOException, FileNotFoundException等异常。我们在访问的代码里实际上是需要采用两种方式来处理的。一种是在使用改资源的方法头增加throws IOException, FileNotFoundException等异常的修饰。还有一种是直接在这部分的代码块增加try/catch部分。由前面我们的讨论已经发现,在方法声明加throws Exception的方式是行不通的。那么就只有使用try/catch这么一种方式了。

    另外,我们也知道,在异常的处理上,一般异常可以分为checked exception和unchecked exception。作为unchecked exception,他们通常是指一些比较严重的系统错误或者系统设计错误,比如Error, OutOfMemoryError或者系统直接就崩溃了。对于这种异常发生的时候,我们一般是无能为力也没法恢复的。那么这种情况的发生,我们会怎么来处理呢?

checked exception

     在线程里面处理checked exception,按照我们以前的理解,我们是可以直接捕捉它来处理的。在一些thread的示例里我们也见过。比如说下面的一部分代码:

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class FileLock implements Runnable {
	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.printf("%s\n", new Date());
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch(InterruptedException e) {
				System.out.printf("The FileClock has been interrupted");
			}
		}
	}
}

     我们定义了一个线程执行代码,并且在这里因为调用TimeUnit.SECONDS.sleep()方法而需要捕捉异常。因为这个方法本身就会抛出InterruptedException,我们必须要用try/catch块来处理。

     我们启动该线程并和它交互的代码如下:

import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) {
		// Creates a FileClock runnable object and a Thread
		// to run it
		FileClock clock=new FileClock();
		Thread thread=new Thread(clock);
		
		// Starts the Thread
		thread.start();
		try {
			// Waits five seconds
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		};
		// Interrupts the Thread
		thread.interrupt();
	}
}

     这部分的代码是启动FileLock线程并尝试去中断它。我们可以发现在运行的时候FileLock里面执行的代码能够正常的处理异常。

    因此,在thread里面,如果要处理checked exception,简单的一个try/catch块就可以了。

unchecked exception

     对于这种unchecked exception,相对来说就会不一样一点。实际上,在Thread的定义里有一个实例方法:setUncaughtExceptionHandler(UncaughtExceptionHandler). 这个方法可以用来处理一些unchecked exception。那么,这种情况的场景是如何的呢?

    setUncaughtExceptionHandler()方法相当于一个事件注册的入口。在jdk里面,该方法的定义如下:

public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
    checkAccess();
    uncaughtExceptionHandler = eh;
}

     而UncaughtExceptionHandler则是一个接口,它的声明如下:

public interface UncaughtExceptionHandler {
    /**
     * Method invoked when the given thread terminates due to the
     * given uncaught exception.
     * <p>Any exception thrown by this method will be ignored by the
     * Java Virtual Machine.
     * @param t the thread
     * @param e the exception
    */
    void uncaughtException(Thread t, Throwable e);
}

     在异常发生的时候,我们传入的UncaughtExceptionHandler参数的uncaughtException方法会被调用。

     综合前面的讨论,我们这边要实现handle unchecked exception的方法的具体步骤可以总结如下:

1. 定义一个类实现UncaughtExceptionHandler接口。在实现的方法里包含对异常处理的逻辑和步骤。

2. 定义线程执行结构和逻辑。这一步和普通线程定义一样。

3. 在创建和执行改子线程的方法里在thread.start()语句前增加一个thread.setUncaughtExceptionHandler语句来实现处理逻辑的注册。

    下面,我们就按照这里定义的步骤来实现一个示例:

    首先是实现UncaughtExceptionHandler接口部分:

import java.lang.Thread.UncaughtExceptionHandler;

public class ExceptionHandler implements UncaughtExceptionHandler {
	public void uncaughtException(Thread t, Throwable e) {
		System.out.printf("An exception has been captured\n");
		System.out.printf("Thread: %s\n", t.getId());
		System.out.printf("Exception: %s: %s\n", 
				e.getClass().getName(), e.getMessage());
		System.out.printf("Stack Trace: \n");
		e.printStackTrace(System.out);
		System.out.printf("Thread status: %s\n", t.getState());
	}
}

     这里我们添加的异常处理逻辑很简单,只是把线程的信息和异常信息都打印出来。

    然后,我们定义线程的内容,这里,我们故意让该线程产生一个unchecked exception:

public class Task implements Runnable {

	@Override
	public void run() {
		int number0 = Integer.parseInt("TTT");
	}
}

     从这代码里我们可以看到,Integer.parseInt()里面的参数是错误的,肯定会抛出一个异常来。

    现在,我们再把创建线程和注册处理逻辑的部分补上来:

public class Main {
	public static void main(String[] args) {
		Task task = new Task();
		Thread thread = new Thread(task);
		thread.setUncaughtExceptionHandler(new ExceptionHandler());
		thread.start();
	}
}

     现在我们去执行整个程序,会发现有如下的结果:

An exception has been captured
Thread: 8
Exception: java.lang.NumberFormatException: For input string: "TTT"
Stack Trace: 
java.lang.NumberFormatException: For input string: "TTT"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:492)
	at java.lang.Integer.parseInt(Integer.java:527)
	at Task.run(Task.java:5)
	at java.lang.Thread.run(Thread.java:722)
Thread status: RUNNABLE

    这部分的输出正好就是我们前面实现UncaughtExceptionHandler接口的定义。

    因此,对于unchecked exception,我们也可以采用类似事件注册的机制做一定程度的处理。

总结

     Java thread里面关于异常的部分比较奇特。你不能直接在一个线程里去抛出异常。一般在线程里碰到checked exception,推荐的做法是采用try/catch块来处理。而对于unchecked exception,比较合理的方式是注册一个实现UncaughtExceptionHandler接口的对象实例来处理。这些细节的东西如果没有碰到过确实很难回答。

参考材料

Java 7 Concurrency Cookbook

http://stackoverflow.com/questions/1369204/how-to-throw-a-checked-exception-from-a-java-thread/1369828#1369828

http://stackoverflow.com/questions/6546193/how-to-catch-an-exception-from-a-thread

分享到:
评论

相关推荐

    java 多线程异常处理

    Java多线程异常处理是Java编程中不可或缺的一部分,特别是在并发编程场景下,正确处理异常能够保证程序的稳定性和健壮性。本实验报告主要涵盖了Java异常处理机制、多线程概念与实现,以及多线程同步问题。 首先,...

    java线程异常

    线程池(Thread Pool)是Java中管理线程的一种高效策略,由`java.util.concurrent`包中的`ExecutorService`和`ThreadPoolExecutor`类提供支持。线程池可以减少频繁创建和销毁线程的开销,提高响应速度,同时提供了一...

    Java处理InterruptedException异常的理论与实践

    在Java编程中,`InterruptedException`是一个特殊的异常类型,主要用于处理线程中断的情况。当你在代码中调用诸如`Thread.sleep()`, `Thread.join()`, 或 `Object.wait()`等阻塞方法时,如果线程被中断,这些方法会...

    java_thread_demo

    在Java中,线程可以通过两种方式创建:继承Thread类或者实现Runnable接口。继承Thread类时,你需要重写run()方法,并直接创建Thread对象来启动线程。而实现Runnable接口则更灵活,因为Java不支持多重继承,所以这种...

    Java Thread Programming (Sams) java线程编程(含code)

    综上所述,"Java Thread Programming (Sams)"涵盖了Java线程编程的各个方面,包括基础概念、创建与管理、同步机制、异常处理、线程池和高级特性。通过阅读和实践其中的代码,开发者能深入理解Java线程编程的精髓,...

    Java线程(Java.Thread)(中英版)

    总的来说,这个资源包为学习和深入理解Java线程提供了全面的材料,从基础概念到高级并发策略,涵盖了一个Java开发人员在实际工作中可能遇到的所有线程相关问题。无论是初学者还是有经验的开发者,都可以从中受益,...

    2024年java面试题-java异常方面的面试题

    ### 2024年Java面试题:Java异常方面的面试题 #### 核心知识点解析 **一、Java异常架构** ...通过以上内容的深入理解,你可以更好地准备Java面试中关于异常处理的部分,并在实际开发工作中有效地运用这些知识。

    java异常处理

    Java异常处理是Java编程中必不可少的一部分,它用于处理程序运行时可能出现的错误和异常情况。异常是程序执行过程中遇到的不正常情况,比如除以零、文件未找到、网络连接失败等。Java通过异常处理机制来确保程序在...

    JAVA异常大全

    Java异常处理是编程过程中的重要组成部分,它帮助开发者在程序执行期间识别并处理错误和意外情况。Java异常是程序运行时出现的不正常状态,它们通常由Java虚拟机(JVM)或者Java类库在遇到特定问题时抛出。异常分为...

    java Thread

    【Java线程详解】 在Java编程中,线程(Thread)是...同时,理解JVM对线程的调度以及线程安全问题的处理也是提升Java并发编程能力的关键。在实际开发中,应结合具体需求灵活运用各种线程控制手段,以实现最佳性能。

    全局异常

    - **异常分类**:区分可预见的和不可预见的异常,对不同类型的异常采取不同的处理策略。 - **避免过度使用全局异常**:全局异常处理应该作为最后的防线,而不是替代局部的异常处理。局部处理能更精确地捕获和修复...

    Java_Thread应该注意的问题

    在Java编程中,线程是并发执行任务的基本单元,有效地利用线程可以提高程序的执行效率。然而,不正确的线程使用可能导致各种问题,如数据不一致、死锁等。以下是一些Java线程编程中需要注意的关键点: 1. **同步...

    android 异常处理机制

    以下将详细介绍Android异常处理机制及其优化策略。 首先,Android系统在遇到未被捕获的异常时,会触发系统默认的错误报告,弹出“应用无响应”(ANR)对话框,这不仅对用户不友好,也无助于开发者定位问题。为了...

    java故障排查ThreadDump

    以下是对如何进行Java故障排查,特别是利用Thread Dump进行问题定位的详细说明: 1. **获取Thread Dump** - 使用JDK自带的`jstack`工具:通过命令行执行`jstack &lt;pid&gt;`,其中`pid`是Java进程的ID,可以得到应用...

    SAMS Java Thread Programming

    - **异步任务处理**:介绍如何在Swing应用程序中处理耗时的任务而不阻塞UI。 5. **第10章:线程组**(Thread Groups) - **线程组结构**:讲解线程组的概念及其实现机制。 - **线程管理**:讨论如何使用线程组来...

    java异常汇总.txt

    在Java编程中,异常处理是一种重要的机制,用于处理程序执行过程中可能出现的错误或异常情况。Java中的异常分为两大类:**受检异常**(Checked Exceptions)和**非受检异常**(Unchecked Exceptions)。受检异常是在...

    android app异常收集处理

    1. **try-catch-finally**:这是Java中最基本的异常处理方式,通过try块包围可能抛出异常的代码,catch块捕获异常并进行处理,finally块确保无论是否发生异常都会执行的代码。 2. **全局异常捕获**:在应用程序的主...

    Android 捕获全局异常处理

    在Android开发中,确保应用程序的稳定性和用户体验是至关重要的。当程序出现未预期的异常时,如果不进行妥善...在实际项目中,开发者应根据需求选择合适的处理策略,并确保在处理异常时不会影响到其他功能的正常运行。

    java 空指针异常(NullPointerException)

    理解并妥善处理`NullPointerException`是Java编程的基本功,良好的编程习惯和对null的理解能帮助我们编写更健壮的代码,避免这种常见的运行时错误。在实际开发中,不断实践和学习,才能更好地应对各种异常情况。

Global site tag (gtag.js) - Google Analytics