【问题标题】:How do asynchronous lambdas in Java scope to local variablesJava范围内的异步lambda如何处理局部变量
【发布时间】:2016-07-23 00:05:13
【问题描述】:

我有一个方法(可以被不同的线程同时调用)创建一个异步任务并返回一个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 如何处理变量范围?

【问题讨论】:

  • 请重新阅读您的问题并进行编辑。措辞不是很清楚。

标签: java multithreading asynchronous lambda


【解决方案1】:

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

Lambdas 不需要用匿名类来实现,但你可以在概念上将它们视为匿名类的语法糖。您的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);
    }
});

问题是,对于 lambda,变量捕获并不是什么新鲜事。匿名类也捕获变量,并且在 lambda 出现之前就已经做到了。发生的情况是,他们偷偷地隐藏了捕获变量的副本。他们获得合成的私有字段来存储这些隐藏的值。如果我们明确合成字段,上面的代码实际上更像这样:

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 变量早已死去并被掩埋。

【讨论】:

    猜你喜欢
    • 2011-11-21
    • 2014-03-02
    • 1970-01-01
    • 2016-11-13
    • 2013-12-25
    • 2017-03-05
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    相关资源
    最近更新 更多