我可以将Scala中的局部变量设置为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进行反编译.这是测试方法的相关部分: 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的运行时表示形式并包装了一个布尔值.有关上一个声明的更多信息,您可以查看related Java documentation. 现在为揭示:如果我们再次使doNotStop变得不稳定怎么办? 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.猜猜它的内部布尔值是如何实现的: volatile public boolean elem; 这里的语义非常清楚:您的非局部布尔变量在运行时表示为BooleanRef实例的字段.正是这个字段可能被注释标记为易失性.你去吧,@ volatile在那里很有用! 回答你的第二个问题:Java的闭包仅接近“有效最终”的值,这将禁止这种模式,其中doNotStop的值在闭包内发生变化.您当然可以使用与此处所做的相同的方式实现它,使用对(易失性)BooleanRef的“有效最终”引用,其elem可以由闭包自由修改. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |