很多时候,在应用程序运行期间你需要一个类来执行一些像数据处理、侦听事件或者检查另一个类活动情况的任务。你可能要通过一组锁定和告知来使用线程从而实现这一点。虽然Java线程API具有丰富的文档说明,但是还是需要大量的代码和经验才能让你的线程正确地和有效地工作。你可以使用暂存来避免每次需要的时候都必须编写这样的类,你也可以使用本文中将讨论的框架来创建一个更加稳健的应用程序。
下载本文所要使用的源代码。
长期运行任务的主要特点是:在应用程序运行期间它必须要一直保持运行。要实现这一点的正确方法是为特定任务提供一个执行线程。你需要创建一个任务,将其作为一个线程或者作为java.lang.Runnable接口的一个实现。如果实现了Runnable,你就可以获得面向对象更好的设计,还可避免单方继承的问题。你也可以更加有效地操控Runnable实例,例如,使用一个线程池来运行,这个池通常需要一个Runnable实例,而不是一个线程。
框架的实质就是Worker抽象类(Listing A),它是用来实现Runnable接口,并为有效地处理任务提供Helper方法的。有些方法是完全实现的,比如run()方法,但是有的是抽象的,需要由你来填充。如果要创建一个长期运行的类,你只需要扩展Worker类,并实现几个抽象方法就行了。现在我们更加仔细地看看这些方法。
Worker类的run()方法是设计用来持续执行work()方法直到其停止的。work()能够负责数据处理、对某个事件做出反应、读取或者编写文件、SQL执行等等。它可以丟置例外,所以把它推广出去以及让run()方法来处理它都是好方法。
run()方法有两层try-catch子句:while循环外面和里面。第一个try-catch子句是用于捕捉所有非程序的例外,而且保证run()方法决不会退出。第二个子句会捕捉住任何属于事务逻辑的例外,并做出相应的行为。如果某个等待操作替代了work()方法(例如服务于InputStream或者Socket),那么最好传播InterruptedException。要记住的是,work()方法不需要任何while循环来维持其在应用程序运行期间的运行。Worker能为你做这件事。
run()方法开始的时候,它会调用prepareWorker(),后者是设计用来为长期运行任务(Listing A)准备所需资源的。在这个方法调用的过程中,你可以,例如建立一个数据库的连接,或者打开一个以后要被用到的文件。在这个地方放置一些封锁操作,例如打开一个socket,将会是非常好的,因为它们会在一个单独的线程里完成,这样就不会封锁主执行线程了。
前一个方法相反的方法是releaseWorker(),它在run()即将退出的时候(Listing A)被调用。这里,你可以使用代码来释放这个任务所使用的系统资源,或者进行其他的清除工作。这个方法和java.lang.Object.finalize()相类似,但是它是在线程中止之前被明确调用的。