多线程在Scala中是否可以将本地变量作为Volatile,因为在Java中这是不可能的?
据我所知,Java和Scala中标记为Volatile的字段提供了before关系
在Java中,不可能将方法中的局部变量设置为volatile。然而Scala编译器似乎允许这样的事情,如下面的代码:
def test: Unit = {
@volatile var doNotStop = true
}
它的工作方式是否与Java中的工作方式相同?这些代码的语义是什么?在运行时,它在字节码和JVM中的外观如何
在Java中,如果给闭包这样的变量,它可以被另一个线程修改,因此,它必须是final,对吗
# 1 楼答案
TL;DR:当对局部变量应用
@volatile
注释时,它看起来像是被忽略的,除非该变量可以在闭包内从该局部范围转义为了确保这一点,我们可以签出与以下代码段对应的字节码
使用
scalac
获得的类文件可以使用javap -c -v -p
进行反编译。以下是test
方法的相关部分:请注意,没有与任何易失性访问相关的信息
如果我们选择将
doNotStop
声明为实例变量,那么javap
将显示以下带有清晰volatile标志的字段声明:然而,您对局部变量超出其作用域的担忧是完全正确的!让我们试试这个:
使用
javap -p
(这次不需要查看字节码或标志)可以给出以下定义:您可以看到,闭包已编译成自己的方法
$anonfun$test$1
,该方法采用BooleanRef
。这个BooleanRef
是doNotStop
的运行时表示,并包装了一个boolean
。有关上一次声明的更多信息,请查看related Java documentation现在让我们来揭示一下:如果我们让
doNotStop
再次变得易变呢课程基本上保持不变,但
$anonfun$test$1
现在需要VolatileBooleanRef
。猜猜它的内部boolean
是如何实现的:这里的语义非常清楚:您的非局部
Boolean
变量在运行时表示为BooleanRef
实例的字段。注释可能会将该字段标记为volatile
。好了,@volatile
毕竟在那里很有用回答你的第二个问题:Java的闭包只关闭“实际上是最终的”值,这将不允许这种模式,即
doNotStop
的值在闭包中发生变化。当然,您可以像这里一样实现它,使用对(Volatile)BooleanRef
的“有效最终”引用,其elem
可以通过闭包自由修改