本文介绍了Java中的异步Lambda如何将作用域定义为局部变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a method (can be concurrently called by different threads) that creates an asynchronous task and returns a CompletableFuture. I want to measure the time it takes to run the task by chaining it with a whenComplete(...) as follows:

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如何处理变量作用域?

The lambda that I pass in will be executed asynchronously and the method that I wrote will also be multi-threaded. Will this lambda be able to print out the accurate time? How does Java handle variable scope when the lambda gets executed asynchronously by a different thread?

推荐答案

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

When a lambda captures a variable, you can think of it as the lambda getting a copy of that variable's value. Because only final or effectively final variables can be captured, it is safe for lambdas to copy them. Their values won't change, so there's no danger of the copy being out of sync with the original variable.

Lambda不必使用匿名类实现,但是您可以在概念上将它们视为匿名类的语法糖.您的whenComplete通话等效于:

Lambdas needn't be implemented with anonymous classes, but you can think of them conceptually as syntactic sugar for anonymous classes. Your whenComplete call is equivalent to:

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并不是变量捕获.匿名类也捕获变量,并且它们在lambda出现之前就已经做到了.发生的是,他们秘密地藏匿了捕获变量的副本.他们获得了合成的私有字段来存储这些隐藏的值.如果我们使合成字段明确,则上面的代码实际上更像这样:

The thing is, variable capturing isn't new with lambdas. Anonymous classes also capture variables, and they did it before lambdas came along. What happens is, they secretly stash away copies of captured variables. They get synthetic private fields to store those stashed values. The code above actually is more like this, if we make the synthetic field explicit:

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变量由于死掉而被埋没已经很久了,其行为也仍然可以像预期的那样.

Notice that once this anonymous BiConsumer is instantiated it stands on its own two feet. The body of accept() now refers to an instance variable, not to the captured variable. The anonymous object is not tied to the outer function, nor to the thread in which it was created. accept() can be called at any time and from any thread and will behave as one would expect, even if the original startTime variable is long since dead and buried.

这篇关于Java中的异步Lambda如何将作用域定义为局部变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 01:26