译者:Greenster  校对:沈义扬

Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理。​Thread​类、​Runnable​接口和Java内存管理模型使得多线程编程简单直接。但正如之前提到过的,Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。没有返回值这点稍微有点麻烦。

 不能声明抛出检查型异常则更麻烦一些。​public void run()​方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(译者注:在捕获异常时)以便稍后检查,但也不能保证这个类(译者注:Runnable对象)的所有使用者都读取异常信息。你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了。

但是现在不用担心了,以上的问题终于在1.5中解决了。Callable接口和Future接口的引入以及他们对线程池的支持优雅地解决了这两个问题。

Callable

Callable接口定义了方法​public T call() throws Exception​。我们可以在Callable实现中声明强类型的返回值,甚至是抛出异常。尽管在Executors类中已经有一些方法可以将Runnable对象转换为Callable对象,你最好还是仔细复审现有的Runnable实现或Thread的子类。为什么还要这样做?主要是为了检查和清除因为Runnable无法抛出检查型异常而采用的变通方案。同时,你可能希望利用call()方法直接返回结果的能力,以省去读取值时的类型转换。

Future

下面就将线程池和Callable接口相结合,看能发生怎样的效应。Future是Java 1.5中引入的接口,当你提交一个Callable对象给线程池时,将得到一个Future对象,并且它和你传入的Callable有相同的结果类型声明。这个对象取代了Java 1.5之前直接操作具体Thread实例的做法。过去你不得不用​Thread.join()​或者​Thread.join(long millis)​等待任务完成,而现在你可以像下面的例子那样做。

view source

print?

​01​

​public​​​ ​​class​​​ ​​ServerAcceptingRequestsVerifier ​​​​implements​​​ ​​Callable {​

​02​

​/**​

​03​

​* @return Boolean.TRUE is server is accepting requests​

​04​

​* Boolean.FALSE otherwise​

​05​

​*/​

​06​

​public​​​ ​​Boolean call() ​​​​throws​​​ ​​Exception {​

​07​

​Boolean isAcceptingRequests = ​​​​null​​​​;​

​08​

​... ask server about taking requests here​

​09​

​return​​​ ​​isAcceptingRequests;​

​10​

​}​

​11​

​}​

​12​

​public​​​ ​​Boolean isServerTakingRequests(String server)​

​13​

​throws​​​ ​​UnresponsiveException, InterruptedException {​

​14​

​ServerAcceptingRequestsVerifier acceptingRequestsVerifier =​

​15​

​new​​​ ​​ServerAcceptingRequestsVerifier();​

​16​

​Future future =​

​17​

​THREAD_POOL.submit(acceptingRequestsVerifier);​

​18​

​try​​​ ​​{​

​19​

​Boolean isAcceptingRequests = future.get();​

​20​

​//waits for the thread to complete, even if it hasn't started​

​21​

​return​​​ ​​isAcceptingRequests;​

​22​

​} ​​​​catch​​​ ​​(ExecutionException e) {​

​23​

​throw​​​ ​​new​​​ ​​UnresponsiveException(e.getCause());​

​24​

​}​

​25​


​26​

​}​

如果要限制等待任务结束的时间,也可以添加一个捕获​TimeoutException​的catch子句​

​01​

​try​​​ ​​{​

​02​

​Boolean isAcceptingRequests = future.get(​​​​5​​​​, TimeUnit.SECONDS);​

​03​

​//this waits for 5 seconds, throwing TimeoutException if not done​

​04​

​return​​​ ​​isAcceptingRequests;​

​05​

​} ​​​​catch​​​ ​​(TimeoutException e) {​

​06​

​LOGGER.warn(​​​​"Timed out waiting for server check thread."​​​ ​​+​

​07​

​"We'll try to interrupt it."​​​​);​

​08​

​future.cancel(​​​​true​​​​);​

​09​

​return​​​ ​​Boolean.FALSE;​

​10​

​} ​​​​catch​​​ ​​(ExecutionException e) {​

​11​

​throw​​​ ​​new​​​ ​​UnresponsiveException(e.getCause());​

​12​

​}​