我正在阅读有关Lambda表达式的this精彩文章,以下内容对我来说未清除:
不要指望编译器捕获所有并发访问错误。该 禁止变异只适用于局部变量。
我不确定自我试验会涵盖所有情况,因此我正在寻找有关以下内容的明确规则:
不要指望编译器捕获所有并发访问错误。该 禁止变异只适用于局部变量。 如果 matchesis是封闭类的实例或静态变量 即使结果是未定义的,也不会报告错误。
更新1:
自由变量 - 即不是参数且未在代码中定义的变量。
简单来说,我可以得出结论,自由变量是不是Lambda表达式参数的所有变量,并且没有在同一个Lambda表达式中定义?
答案 0 :(得分:6)
你的术语“自由变量”充其量只是误导。如果你不是在讨论局部变量(必须有效地最终被捕获),那你就是在讨论堆变量。
堆变量可能是实例字段,static
字段或数组元素。对于从周围上下文中对实例变量的非限定访问,lambda表达式可以(并将)通过捕获的this
引用访问它们。对于其他实例字段以及数组元素,无论如何都需要通过变量进行显式访问,因此很清楚,如何访问堆变量。只能直接访问static
个字段。
规则很简单,除非声明为final
,否则可以在lambda表达式内部或外部修改所有规则。请记住,lambda表达式可以调用任意方法,无论如何都包含任意代码。这是否会导致问题,取决于您如何使用lambda表达式。您甚至可以创建不直接修改变量的函数的问题,而不需要任何并发性,例如
ArrayList<String> list=new ArrayList<>(Arrays.asList("foo", "bar"));
list.removeIf(s -> list.remove("bar"));
由于正在进行的迭代中的列表修改,可能会抛出java.util.ConcurrentModificationException
。
同样,修改并发上下文中的变量或资源可能会破坏它,即使您确保变量本身的修改是以线程安全的方式完成的。这都是关于您正在使用的API的合同。
最值得注意的是,在使用并行Streams时,您必须意识到函数不仅由不同的线程进行评估,它们还在评估Stream的任意元素,而不管它们的遭遇顺序如何。对于Stream处理的最终结果,实现将以重新建立遭遇顺序的方式组合部分结果,如果需要,但是中间操作以任意顺序评估元素,因此您的函数不仅必须是线程安全的,而且也不依赖于特定的处理订单。在某些情况下,他们甚至可能处理对最终结果没有贡献的元素。
由于你的子弹3指的是“在一个块结束之后”,我想强调一下,你的lambda表达式中的哪个位置修改(或可感知的副作用)发生是无关紧要的。
一般来说,你最好使用没有这种副作用的功能。但这并不意味着他们一般都被禁止。