Callable和Future

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)等待任务完成,而现在你可以像下面的例子那样做。

public class ServerAcceptingRequestsVerifier implements Callable {
	/**
	 * @return Boolean.TRUE is server is accepting requests
	 * Boolean.FALSE otherwise
	 */
	public Boolean call() throws Exception {
		Boolean isAcceptingRequests = null;
		... ask server about taking requests here
		return isAcceptingRequests;
	}
}
public Boolean isServerTakingRequests(String server)
			throws UnresponsiveException, InterruptedException {
	ServerAcceptingRequestsVerifier acceptingRequestsVerifier =
		new ServerAcceptingRequestsVerifier();
	Future future =
		THREAD_POOL.submit(acceptingRequestsVerifier);
	try {Boolean isAcceptingRequests = future.get();
		//waits for the thread to complete, even if it hasn't started
		return isAcceptingRequests;
	} catch (ExecutionException e) {throw new UnresponsiveException(e.getCause());
	}

}

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

try {
Boolean isAcceptingRequests = future.get(5, TimeUnit.SECONDS);
//this waits for 5 seconds, throwing TimeoutException if not done
return isAcceptingRequests;
} catch (TimeoutException e) {
LOGGER.warn("Timed out waiting for server check thread." +
"We'll try to interrupt it.");
future.cancel(true);
return Boolean.FALSE;
} catch (ExecutionException e) {
throw new UnresponsiveException(e.getCause());
}