java.util.concurrent.Future基础

在此,我开始撰写一系列有关编程语言中的未来概念(也称为promisedelays )的文章,标题为: Back to the Future 。 由于对异步,事件驱动,并行和可伸缩系统的需求不断增长,所以期货是非常重要的抽象,如今比以往任何时候都更加重要。 在第一篇文章中,我们将发现最基本的java.util.concurrent.Future<T>接口。 稍后,我们将跳入其他框架,库甚至语言。 Future<T>是相当有限的,但必须了解,ekhm,将来部分。

在单线程应用程序中,当您调用方法时,它仅在计算完成IOUtils.toString()返回( IOUtils.toString()来自Apache Commons IO ):

public String downloadContents(URL url) throws IOException {
    try(InputStream input = url.openStream()) {
        return IOUtils.toString(input, StandardCharsets.UTF_8);
    }
}
 
//...
 
final String contents = downloadContents(new URL("http://www.example.com"));

downloadContents()看起来是无害的1 ,但它甚至可能需要花费很长时间才能完成。 此外,为了减少延迟,您可能希望在等待结果的同时进行其他独立的处理。 在过去,您将启动一个新Thread并以某种方式等待结果(共享内存,锁,可怕的wait() / notify()对等),使用Future<T>会更加愉快:

public static Future<String> startDownloading(URL url) {
    //...
}
 
final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
//other computation
final String contents = contentsFuture.get();

我们将很快实现startDownloading() 。 目前,重要的是要了解这些原理。 startDownloading() 阻塞,等待外部网站。 相反,它会立即返回,并返回轻量级的Future<String>对象。 此对象保证 String将来会可用。 不知道什么时候,但是保留此引用,一旦存在,您就可以使用Future.get()检索它。 换句话说, Future是一个尚未存在的对象的代理或包装。 异步计算完成后,您可以提取它。 那么Future提供了什么API?

Future.get()是最重要的方法。 它阻塞并等待,直到承诺的结果可用(已解决 )。 因此,如果我们确实需要该String ,则只需调用get()并等待。 有一个过载的版本可以接受超时,所以一旦出现问题,您将不会永远等待。 如果等待时间太长,则会抛出TimeoutException

在某些用例中,您可能希望窥视“ Future ,如果结果尚不可用,请继续。 使用isDone()可以做到这一点。 想象一下这样一种情况,您的用户正在等待一些异步计算,而您想让他知道我们仍在等待并同时进行一些计算:

final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
while (!contentsFuture.isDone()) {
    askUserToWait();
    doSomeComputationInTheMeantime();
}

到最后调用contentsFuture.get()是保证立即返回,而不是因为块Future.isDone()返回true 。 如果遵循上述模式,请确保您不忙于等待,每秒每秒调用isDone()数百万次。

取消期货是我们尚未讨论的最后一个方面。 想象一下,您开始了一些异步作业,并且您只能在给定的时间内等待它。 如果2秒钟后仍没有出现,我们会放弃并传播错误或解决它。 但是,如果您是一个好公民,您应该以某种方式告诉这个未来的目标:我不再需要您,那就算了。 通过不运行过时的任务来节省处理资源。 语法很简单:

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

我们都喜欢神秘的布尔参数,不是吗? 取消有两种口味。 通过将false传递给mayInterruptIfRunning参数,当Future表示尚未开始的计算结果时,我们仅取消尚未开始的任务。 但是,如果我们的Callable.call()已经在中间,则让它结束。 但是,如果我们传递true ,则Future.cancel()将更具攻击性,并尝试中断已经在运行的作业。 怎么样? 考虑所有引发臭名昭著的InterruptedException方法,即Thread.sleep()Object.wait()Condition.await()以及许多其他方法(包括Future.get() )。 如果您禁止使用任何此类方法,而某人决定取消您的Callable ,则它们实际上将抛出InterruptedException ,表示有人试图中断当前正在运行的任务。

因此,我们现在了解什么是Future<T> –将来会得到的东西的占位符。 这就像尚未生产的汽车的钥匙。 但是,实际上如何在应用程序中获取Future<T>的实例? 两种最常见的来源是线程池和异步方法(由您的线程池支持)。 因此,我们的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);
            }
        }
    });
}

语法很多,但基本思想很简单:将长时间运行的计算包装在Callable<String>然后submit()它们submit()到10个线程的线程池中。 提交返回Future<String>某些实现,最有可能以某种方式链接到您的任务和线程池。 显然,您的任务不会立即执行。 而是将其放置在队列中,该队列稍后(甚至可能更晚)由池中的线程轮询。 现在应该清楚这两种cancel()含义–您始终可以取消仍然驻留在该队列中的任务。 但是取消已经运行的任务要复杂一些。

可以遇到Future另一个地方是Spring和EJB。 例如,在Spring框架中,您可以使用@Async注释您的方法

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

注意,我们只是将结果包装在实现Future AsyncResult 。 但是该方法本身不处理线程池或异步处理。 Spring稍后将代理所有对startDownloading()调用,并在线程池中运行它们。 EJB中的@Asynchronous批注提供了完全相同的功能。

因此,我们了解了很多有关java.util.concurrent.Future 。 现在该承认了–该界面非常有限,尤其是与其他语言相比时。 以后再说。

1 –您不熟悉Java 7的try-with-resources功能吗? 您现在最好切换到Java 7。 Java 6将在两周内不再维护。

参考: NoBlogDefFound博客上来自我们的JCG合作伙伴 Tomasz Nurkiewicz的java.util.concurrent.Future基础

翻译自: https://www.javacodegeeks.com/2013/02/java-util-concurrent-future-basics.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值