Java中的异步lambdas如何作用于局部变量

时间:2016-07-23 00:05:13

标签: java multithreading asynchronous lambda

我有一个方法(可以由不同的线程同时调用),它创建一个异步任务并返回CompletableFuture。我想通过使用whenComplete(...)链接它来测量运行任务所需的时间,如下所示:

public CompletableFuture<Result> createTask(...) {
    CompletableFuture<Result> result = ...;
    final long startTime = System.currentTimeMillis();
    result.whenComplete((res, err) -> {
        System.out.println(System.currentTimeMillis() - startTime);
    }
}

我传入的lambda将以异步方式执行,我编写的方法也将是多线程的。这个lambda能打印出准确的时间吗?当lambda由另一个线程异步执行时,Java如何处理变量范围?

1 个答案:

答案 0 :(得分:4)

当lambda捕获变量时,您可以将其视为lambda获取该变量值的副本。因为只能捕获最终或有效的最终变量,所以lambdas可以安全地复制它们。它们的值不会改变,因此副本不会与原始变量不同步。

Lambda不需要使用匿名类来实现,但您可以将它们概念性地视为匿名类的语法糖。您的whenComplete来电相当于:

long startTime = System.currentTimeMillis();

result.whenComplete(new BiConsumer<T, U>() {
    @Override public void accept(T res, U err) {
        System.out.println(System.currentTimeMillis() - startTime);
    }
});

事实上,变量捕获对于lambdas来说并不新鲜。匿名类也捕获变量,并且在lambdas出现之前就已经完成了。会发生什么,他们秘密地藏匿捕获变量的副本。他们获得合成私有字段来存储那些存储的值。如果我们将合成字段显式化,上面的代码实际上更像是这样:

long startTime = System.currentTimeMillis();

result.whenComplete(new BiConsumer<T, U>() {
    private final long _startTime = startTime;

    @Override public void accept(T res, U err) {
        System.out.println(System.currentTimeMillis() - _startTime);
    }
});

请注意,一旦这个匿名BiConsumer被实例化,它就会站在自己的两只脚上。 accept()的主体现在引用实例变量,而不是捕获的变量。匿名对象不依赖于外部函数,也不依赖于创建它的线程。 accept()可以在任何时间从任何线程调用,并且可以按照预期的方式运行,即使原始的startTime变量已经很久没有被埋葬了。

相关问题