当前位置: 首页 > 知识库问答 >
问题:

为什么实例字段不需要是final字段或有效地final字段才能在lambda表达式中使用?

隆飞宇
2023-03-14

我在Java练习lambda表情。我知道局部变量需要是最终的,或者根据Java SE16 Lambda Body的Oracle文档,实际上是最终的:

在lambda表达式中使用但未声明的任何局部变量、形参或异常参数必须是final或有效final(§4.12.4),如§6.5.6.1中所规定的。

但上面没有说明原因。搜索我发现了这个类似的问题,为什么lambdas中的变量必须是final或者实际上是final的?StackOverflow用户“snr”用下一个引号回应了这个问题:

到目前为止,Java中的局部变量不受竞争条件和可见性问题的影响,因为它们只能被执行声明它们的方法的线程访问。但是lambda可以从创建它的线程传递给另一个线程,因此,如果第二个线程评估的lambda被赋予了变异局部变量的能力,那么这种免疫力就会丧失。

  • 来源:为什么限制局部变量捕获?

这就是我所理解的:一个方法一次只能由一个线程(假设thread_1)执行。这确保了该特定方法的局部变量仅由Thread_1修改。另一方面,一个lambda可以传递给一个不同的线程(thread_2),所以...如果thread_1以lambda表达式结束并继续执行方法的其余部分,它可能会更改局部变量的值,同时thread_2也可能会更改lambda表达式中的相同变量。那么,这就是为什么存在这个限制(局部变量需要是final或者有效final)。

抱歉解释得太长了。我说对了吗?

但接下来的问题是:

  • 为什么这种情况不适用于实例变量?
  • 如果thread_1与thread_2同时更改实例变量(即使它们不执行lambda表达式),会发生什么情况?
  • 实例变量是否以另一种方式受到保护?

我对Java没有太多经验。抱歉,如果我的问题有明显的答案。

共有1个答案

牟黎昕
2023-03-14

实例变量存储在堆空间中,而局部变量存储在堆栈空间中。每个线程都维护自己的堆栈,因此局部变量不在线程之间共享。另一方面,堆空间由所有线程共享,因此,多个线程可以修改实例变量。有各种机制使数据线程安全,您可以在这个平台上找到许多相关的讨论。为了完整起见,我在下面引用了http://web.mit.edu/6.005/www/fa14/classes/18-thread-safety/

基本上有四种方法可以使共享内存并发中的变量访问安全:

  • 禁闭。不要在线程之间共享变量。这个想法被称为禁闭,我们今天将探讨它。
  • 不变性。使共享数据不可变。我们已经讨论了很多关于不可变性的问题,但是对于并发编程还有一些附加的限制,我们将在本阅读中讨论。
  • 线程安全数据类型。将共享数据封装在为您进行协调的现有线程安全数据类型中。我们今天会讨论这个问题。
  • 同步。使用同步来防止线程同时访问变量。同步是构建自己的线程安全数据类型所需要的。
 类似资料:
  • lambda表达式中使用的变量应为final或有效final 当我尝试使用时,它显示了这个错误。

  • 这个问题以前在这里问过 我关于为什么在这里回答的问题 但我对这个答案有一些怀疑。提供的答案提到- 为了降低bug的风险,他们决定确保捕获的变量永远不会变异。我对它会导致并发问题的说法感到困惑。 我在Baeldung上读了一篇关于并发问题的文章,但我仍然对它如何导致并发问题感到有点困惑,有人能用一个例子来帮助我吗?提前谢了。

  • 如果我在一个类中有以下语句,其中Synapse是一个抽象类型: 最终是否允许我仍然能够更改列表中Synapse对象的状态,但阻止我向列表中添加新的Synapse对象?如果我错了,请您解释一下final正在做什么,以及我应该在什么时候使用关键字final。

  • 我试图通过反射在Java17覆盖非静态最终字段的值。 据我所知,从Java 12及以上版本开始,你就不能再做下面这一招了: 当我在Java 17中运行时,会引发以下异常: “bar”的值保持不变。 对于Java的新版本,是否有等效的方法覆盖最终字段?在谷歌中的快速搜索没有产生与上述解决方案不同的任何结果。我唯一学到的是,覆盖静态最终字段是不可能的,而通过反射仍然可以覆盖非静态最终字段,但我找不到方

  • 问题内容: 当超类具有标记为final的字段但子类覆盖(隐藏?)此字段时,会发生什么?“最终”并没有阻止它,不是吗?我正在处理的特定示例是Building类,从中继承了各种建筑物。除其他事项外,每种类型的成本对于每个子类都应是最终成本,但是每种类型的建筑物应具有自己的成本。 编辑:从那以后,我意识到我不知道上面我在说什么。我真正想要的是成本的静态变量。但是,如果我在超类中声明这些静态变量,则它们对

  • 问题内容: 我对Go之类的打字语言还是陌生的,并且正在尝试学习实现事物的最佳方法。 我有两个表示将插入到mongodb数据库中的模型的结构。一个结构(投资)具有另一个结构(组)作为其字段之一。 我遇到的问题是在投资模型中,不需要组。如果没有组,我认为最好不要将其插入数据库。在Go中处理这种数据库模型的最佳方法是什么? 问题答案: tl; dr :使用,如果您需要担心零值和null /未指定之间的差