Jos*_*eph 2 java lambda multithreading asynchronous
我有一个方法(可以由不同的线程同时调用)创建一个异步任务并返回一个CompletableFuture
. 我想通过将任务与 whenComplete(...) 链接来测量运行任务所需的时间,如下所示:
public CompletableFuture<Result> createTask(...) {
CompletableFuture<Result> result = ...;
final long startTime = System.currentTimeMillis();
result.whenComplete((res, err) -> {
System.out.println(System.currentTimeMillis() - startTime);
}
}
Run Code Online (Sandbox Code Playgroud)
我传入的 lambda 将异步执行,我编写的方法也将是多线程的。这个 lambda 能够打印出准确的时间吗?当 lambda 被不同的线程异步执行时,Java 如何处理变量范围?
当 lambda 捕获一个变量时,您可以将其视为 lambda 获取该变量值的副本。因为只能捕获 final 或有效的 final 变量,所以 lambda 复制它们是安全的。它们的值不会改变,因此不会有副本与原始变量不同步的危险。
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);
}
});
Run Code Online (Sandbox Code Playgroud)
问题是,变量捕获对于 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);
}
});
Run Code Online (Sandbox Code Playgroud)
请注意,一旦这个匿名BiConsumer
被实例化,它就站在自己的两只脚上。accept()
now的主体指的是一个实例变量,而不是捕获的变量。匿名对象不绑定到外部函数,也不绑定到创建它的线程。accept()
可以在任何时间从任何线程调用,并且会像人们预期的那样运行,即使原始startTime
变量早已死亡并被掩埋。
归档时间: |
|
查看次数: |
1671 次 |
最近记录: |