java - 我可以在 Scala 中将本地 var 作为 Volatile,因为在 Java 中这是不可能的吗?
问题描述
据我所知,Java 和 Scala 中标记为 Volatile 的字段提供了发生之前的关系。
在 Java 中,方法中的局部变量不可能是 volatile。然而,Scala 编译器似乎允许这样的事情,如下面的代码:
def test: Unit = {
@volatile var doNotStop = true
}
它实际上与Java中的工作方式相同吗?这种代码的语义是什么?它在运行时在字节码和 JVM 中的外观如何?
在Java中,如果给闭包这样的变量,可以由另一个线程修改,因此,它是强制性的,对吧?
解决方案
TL;DR:@volatile
注释在应用于局部变量时看起来像是被忽略了,除非变量可以在闭包内从这个局部范围内转义。
为了确保这一点,我们可以检查与以下代码片段对应的字节码
class Foo {
def test: Unit = {
@volatile var doNotStop: Boolean = true
}
}
使用 获得的类文件scalac
可以使用javap -c -v -p
. 这是该test
方法的相关部分:
public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: iconst_1
1: istore_1
2: return
LocalVariableTable:
Start Length Slot Name Signature
1 1 1 doNotStop Z
...
请注意,没有与任何易失性访问相关的信息。
如果我们选择声明doNotStop
为实例变量,则javap
显示以下带有明确 volatile 标志的字段声明:
private volatile boolean doNotStop;
descriptor: Z
flags: ACC_PRIVATE, ACC_VOLATILE
但是,您对局部变量超出其范围的担忧是完全正确的!让我们试试这个:
class Foo {
def test = {
var doNotStop: Boolean = true
() => doNotStop = false
}
}
使用javap -p
(这次无需查看字节码或标志)为我们提供了以下定义:
public class Foo {
public scala.Function0<scala.runtime.BoxedUnit> test();
public static final void $anonfun$test$1(scala.runtime.BooleanRef);
public Foo();
private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
}
你可以看到闭包已经被编译成它自己的方法$anonfun$test$1
,名为BooleanRef
. 这BooleanRef
是运行时表示doNotStop
并包装了boolean
. 有关最后一个声明的更多信息,您可以查看相关的 Java 文档。
现在揭示:如果我们doNotStop
再次使 volatile 发生怎么办?
public class Foo {
public scala.Function0<scala.runtime.BoxedUnit> test();
public static final void $anonfun$test$1(scala.runtime.VolatileBooleanRef);
public Foo();
private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
}
课程基本保持不变,但$anonfun$test$1
现在需要一个VolatileBooleanRef
. 猜猜它的内部boolean
是如何实现的:
volatile public boolean elem;
这里的语义非常清楚:您的非局部Boolean
变量在运行时表示为BooleanRef
实例的字段。volatile
注释可以标记的正是该字段。你去了,@volatile
毕竟在那里很有用!
要回答您的第二个问题:Java 的闭包仅关闭“有效最终”的值,这将不允许这种模式在doNotStop
闭包内更改值。当然,您可以使用与此处相同的方式来实现它,使用对 a 的“有效最终”引用,(Volatile)BooleanRef
其elem
可以由闭包自由修改。
推荐阅读
- c# - 使用 C# 添加网络打印机
- java - 无法使用 aws lambda 函数将远程存储库(本地)克隆到 S3 存储桶
- function - exec() 命令在纯代码中工作,但不在方法内
- angular - 如何在 angular/drupal 项目中包含与 angular2-jsonapi 的关系
- android - 如何将 int[ ][ ] 转换为 ArrayList
- kotlin - 释放与项目相关的资源
- python - 简单的打印语句在两个 RPI 之一上给出 UnicodeEncodeError
- python - 使用 Airflow 中的另一个 dag 触发外部 dag
- scala - 为 kubernetes 部署构建 fat spark jars 和 bundles
- javascript - 如何获取动态 Angular 字段的控件名称?