带有局部变量捕获的Java lambda表达式

带有局部变量捕获的Java lambda表达式,java,lambda,Java,Lambda,局部变量为final或有效final的原因是由于并发性问题。在jls 8规范中,它说明了以下内容 对有效最终变量的限制禁止访问 动态更改局部变量,其捕获可能 引入并发问题 一切都很好,但我做了一个小实验。如果我同步该方法,将消除动态更改局部变量的可能性,因为我保证只有一个线程可以执行该代码。但是编译抛出了一个错误,说它必须是最终的或实际上是最终的 逻辑正确吗 考虑以下代码: public synchronized void capture() { int localVa

局部变量为final或有效final的原因是由于并发性问题。在jls 8规范中,它说明了以下内容

对有效最终变量的限制禁止访问 动态更改局部变量,其捕获可能 引入并发问题

一切都很好,但我做了一个小实验。如果我同步该方法,将消除动态更改局部变量的可能性,因为我保证只有一个线程可以执行该代码。但是编译抛出了一个错误,说它必须是最终的或实际上是最终的

逻辑正确吗

考虑以下代码:

    public synchronized void capture() {

        int localVariable = 100;

        Interf i = (text) -> System.out.println(text + localVariable);

        i.m1("This local variable is: ");

        localVariable = 1000;
    }
}

答案很简单,您的变量在方法末尾超出范围。这很容易用有效的最终变量解决,因为编译器只需将值复制到lambda中。因为lambda表达式中的代码也可以在方法之外运行(可修改变量已经被垃圾收集),所以这不起作用。您也不能期望编译器以某种方式复制变量,然后在lambda表达式之外对其进行修改时动态更改它。我希望这能把事情弄清楚

但是编译抛出了一个错误,说它必须是最终的或实际上是最终的

那是因为它是按照规则做的。不,是的,没有但是;不管您是否真的防范了所有并发性问题——如果它不是有效的最终版本,它将不会编译


在这里的简单示例中,这可能是正确的。然而,使方法同步是不相关的,因为局部变量始终与它们的每线程调用相关联。编译器担心的是方法本身上下文中的线程问题,而在使用lambda时,这种问题很容易发生(可能在将来的某个任意时间执行,非最终变量的状态可能已经更改,如果更改了,则根本不清楚应该使用什么状态—初始状态或更新状态。)

想象一下,您创建了一个完整的未来,由ForkJoinPool或其他执行器执行


这就是为什么在此方法上进行同步不足以推翻局部变量有效为final的规则。lambda将同步执行,但它创建的异步任务不会同步。

您确定将lambda传递给的方法不使用其他线程吗?还是执行lambda someti我以后?考虑<代码> SWIGUTILLS。。这应该打印什么?有关final/effective final的规则不是由并发引起的。而是由传递值引起的。lambda获得一个副本。如果在lambda中或外部更改副本,两个副本都将具有不同的值。synchronized on方法将确保该方法由单个线程调用。但它不会忽略ee方法本身在其中创建新线程,是吗?假设lambda创建了一个未来,由forkjoinpool或另一个执行器执行。
编译器担心的是方法本身上下文中的线程问题
我知道这是真的,你能给我一个例子来巩固这一点吗假设你提到过,你是说如果lambda表达式可以改变局部变量,那么其他线程就不能获得可见性,因为局部变量是线程私有的