Thu*_*ght 5 java concurrency lambda
这个问题之前已经在这里被问过\n
\n我的问题为什么在这里得到回答\n
\n但是我对答案有一些疑问。\n提供的答案提到-
\n\n\n\n尽管其他答案证明了该要求,但它们没有解释该要求存在的原因。
\n
\n\nJLS 在\xc2\xa715.27.2中提到了原因:
\n
\n\n\n\n对有效最终变量的限制禁止访问动态更改的局部变量,捕获这些变量可能会引入并发问题。
\n
\n\n为了降低错误风险,他们决定确保捕获的变量永远不会发生变化。\n我对这会导致并发问题的说法感到困惑。\n
\n
我在Baeldung上阅读了有关并发问题的文章,但我仍然对它如何导致并发问题感到有点困惑,有人可以帮我举个例子吗。\n提前致谢。
\n我想在这个答案的前言中说,我下面显示的内容实际上并不是 lambda 的实现方式。 java.lang.invoke.LambdaMetafactory 如果我没记错的话,实际的实施涉及到。我的回答利用了一些不准确的地方来更好地证明这一点。
假设您有以下内容:
\npublic static void main(String[] args) {\n String foo = "Hello, World!";\n Runnable r = () -> System.out.println(foo);\n r.run();\n}\nRun Code Online (Sandbox Code Playgroud)\n请记住,lambda 表达式是声明函数接口实现的简写。lambda 主体是所述功能接口的单个抽象方法的实现。在运行时创建一个实际的对象。所以上面的结果是一个类实现了Runnable.
现在,上面的 lambda 主体引用了封闭方法中的局部变量。作为 lambda 表达式的结果创建的实例“捕获”该局部变量的值。几乎(但不是真的)就像您有以下内容:
\npublic static void main(String[] args) {\n String foo = "Hello, World!";\n\n final class GeneratedClass implements Runnable {\n \n private final String generatedField;\n\n private GeneratedClass(String generatedParam) {\n generatedField = generatedParam;\n }\n\n @Override\n public void run() {\n System.out.println(generatedField);\n }\n }\n\n Runnable r = new GeneratedClass(foo);\n r.run();\n}\nRun Code Online (Sandbox Code Playgroud)\n现在应该更容易在这里看到支持并发的问题:
\n局部变量不被视为“共享变量”。Java 语言规范的 \xc2\xa717.4.1中对此进行了说明:
\n\n\n可以在线程之间共享的内存称为共享内存或堆内存。
\n所有实例字段、静态字段和数组元素都存储在堆内存中。在本章中,我们使用术语变量来指代字段和数组元素。
\n局部变量 (\xc2\xa714.4)、形式方法参数 (\xc2\xa78.4.1) 和异常处理程序参数 (\xc2\xa714.20) 永远不会在线程之间共享,并且不受内存模型的影响。
\n
也就是说,局部变量不受Java并发规则的覆盖,不能在线程之间共享。
\n在源代码级别,您只能访问局部变量。您看不到生成的字段。
\n我认为 Java 可以设计为修改 lambda 主体内部的局部变量仅写入生成的字段,而修改 lambda 主体外部的局部变量仅写入局部变量。但正如您可能想象的那样,这会令人困惑且违反直觉。根据源代码,您将有两个变量似乎是一个变量。更糟糕的是,这两个变量的值可能会有所不同。
\n另一种选择是不生成字段。但请考虑以下事项:
\npublic static void main(String[] args) {\n String foo = "Hello, World!";\n Runnable r = () -> {\n foo = "Goodbye, World!"; // won\'t compile\n System.out.println(foo);\n }\n new Thread(r).start();\n System.out.println(foo);\n}\nRun Code Online (Sandbox Code Playgroud)\n这里应该发生什么?如果没有生成字段,则局部变量正在被第二个线程修改。但局部变量不能在线程之间共享。因此,这种方法是不可能的,至少在不对 Java 和 JVM 进行重大更改的情况下是不可能的。
\n因此,据我了解,设计者制定了这样的规则:在这种情况下,局部变量必须是最终的或实际上是最终的,以避免并发问题并使开发人员对深奥的问题感到困惑。
\n| 归档时间: |
|
| 查看次数: |
10445 次 |
| 最近记录: |