简析Java中的util.concurrent.Future接口

时间:2021-05-19

在一个单线程应用中,当你调用一个方法只有计算结束才会返回结果( IOUtils.toString() comes from Apache Commons IO ):

public String downloadContents(URL url) throws IOException { try(InputStream input = url.openStream()) { return IOUtils.toString(input, StandardCharsets.UTF_8); }} //... final Future<String> contents = downloadContents(new URL("http://putationInTheMeantime();}contentsFuture.get();

最后Future.get()调用的内容会保证马上返回,不会被阻塞,因为Future.isDone() 返回了true。如果你遵循这个模式,就不会忙于每秒百万次的交替等待和调用isDone()。


取消futrues是最后一个我们还没有覆盖到的。想象你启动了异步的工作并且你只能等待一些时间, 如果2秒钟后,我们放弃,或者把错误传递出去,或者采用临时方案解决它。然而,你是一个好市民,你应该告诉这个future对象:我不需要你了,你别管了。 那么你可以通过停止过时的任务,来节约资源。语法很简单:

contentsFuture.cancel(true); //meh...


我们都喜欢隐藏的,布尔类型的参数,对吗?取消可以通过两种方式来实现:在任务启动前通过传递false参数来取消,前提是当Future表达的结果计算开始之前。一旦Callable.call()已经运行到一半,那么我们想让它结束,如果我们传递true,那么Future.call()就会具有侵入性,试图打断正在运行的工作。你觉得这样好吗?现象那些抛出InterruptedException这个声名狼藉的异常的方法,如Thread.sleep(), Object.wait(),Condition.await(),等,甚至包括Future.get(). 如果你被阻塞在这种方法并且有人决定取消你的调用,他们会毫无疑问的抛出InterruptionException,并发出有人要打断当前运行的任务。


因此我们现在明白了Future是什么--- 一个占位符,你可以在未来得到目标对象。就像对于一辆车,还没有制造出来的钥匙。但是你怎样才能在应用程序中获得Future的实例? 两种最普通的资源是线程池和异步方法(线程池支持)。因此, startDownloading()方法可以被重写为:

private final ExecutorService pool = Executors.newFixedThreadPool(10); public Future<String> startDownloading(final URL url) throws IOException { return pool.submit(new Callable<String>() { @Override public String call() throws Exception { try (InputStream input = url.openStream()) { return IOUtils.toString(input, StandardCharsets.UTF_8); } } });}


虽然有大量的繁琐的语法问题,但是基本思想是简单的: 把需要长时间运行的计算包装到可调用的<String>,并submit()到线程池,这个线程池包含10个线程。 提交后返回Future<String>的实现,就像以某种方式链接到你的任务和线程池。明显的你的任务不会被立即执行,相反它被放到一个队列中,稍后会被线程拉出来, 现在需要搞清楚cancel()的两个特别的意义是什么——你可以取消在队列中停留的任务,也可以取消早已运行的任务,但这是一件比较复杂的事情。


你还可以在Spring 和 EJB 碰上Future。比如Spring框架的中你可以为方法加入@Async的注解:

@Asyncpublic Future<String> startDownloading(final URL url) throws IOException { try (InputStream input = url.openStream()) { return new AsyncResult<>( IOUtils.toString(input, StandardCharsets.UTF_8) ); }}


注意,我们简单地通过包装结果到AsyncResult来实现Future,但是这个方法本身不会与线程池交互或者异步处理。稍后 Spring会代理所有的调用来startDownloading()并在线程池中执行。 在EJB中,相同的特性通过加@Asynchronousannotation 来完成。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章