我可以将Scala中的局部变量设置为Volatile,因为在Java中它是不可能的吗?

Fil*_*nda 4 java multithreading jvm scala

据我所知,Java和Scala中的字段标记为Volatile,提供了在关系之前发生的事情.

在Java中,不可能在方法中将局部变量设置为volatile.然而,Scala编译器似乎允许这样的事情,如下面的代码所示:

def test: Unit = {
  @volatile var doNotStop = true 
}
Run Code Online (Sandbox Code Playgroud)

它的实际工作方式与Java相同吗?这些代码的语义是什么?它在运行时如何看待字节代码和JVM?

在Java中,如果赋予闭包这样的变量可以被另一个线程修改,因此,它必须是最终的,对吧?

fra*_*isr 6

TL; DR:@volatile注释在应用于局部变量时看起来被忽略,除非变量可以从闭包内的本地范围转义.

为了确保这一点,我们可以检查对应于以下代码段的字节码

class Foo {
    def test: Unit = {
      @volatile var doNotStop: Boolean = true 
    }
}
Run Code Online (Sandbox Code Playgroud)

使用获得的类文件可以使用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
      ...
Run Code Online (Sandbox Code Playgroud)

请注意,没有与任何易失性访问相关的信息.

如果我们选择将其声明doNotStop为实例变量,则javap显示以下带有清除volatile标志的字段声明:

private volatile boolean doNotStop;
  descriptor: Z
  flags: ACC_PRIVATE, ACC_VOLATILE
Run Code Online (Sandbox Code Playgroud)

但是,您对局部变量逃避其范围的关注是完全有效的!我们试试这个:

class Foo {
    def test = {
        var doNotStop: Boolean = true
        () => doNotStop = false
    }
}
Run Code Online (Sandbox Code Playgroud)

使用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);
}
Run Code Online (Sandbox Code Playgroud)

你可以看到闭包已被编译成自己的方法,命名为$anonfun$test$1a BooleanRef.这BooleanRef是doNotStop包含a 的运行时表示boolean.有关最后一个声明的更多信息,您可以查看相关的Java文档.

现在为揭示:如果我们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);
}
Run Code Online (Sandbox Code Playgroud)

班级大致保持不变,但$anonfun$test$1现在需要一个VolatileBooleanRef.猜猜它的内在boolean是如何实现的:

volatile public boolean elem;
Run Code Online (Sandbox Code Playgroud)

这里的语义非常清楚:您的非局部Boolean变量BooleanRef在运行时表示为实例的字段.这个字段可以用volatile注释标记.你走了,@volatile毕竟在那里很有用!

回答你的第二个问题:Java的闭包仅接近"有效最终"的值,这将禁止这种模式,其中doNotStop闭包内的变化值.当然,您可以使用与此处所做的相同的方式实现它,使用"有效最终"引用,(Volatile)BooleanRef其elem可以由闭包自由修改.