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

时间:2018-04-30 20:26:16

标签: java multithreading scala jvm

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

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

def test: Unit = {
  @volatile var doNotStop = true 
}

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

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

1 个答案:

答案 0 :(得分:6)

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会显示以下带有清晰易失标记的字段声明:

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。此BooleanRefdoNotStop的运行时表示形式,并包含boolean。有关上一个声明的更多信息,您可以查看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。猜猜它的内部boolean是如何实现的:

volatile public boolean elem;

这里的语义非常明确:您的非本地Boolean变量在运行时表示为BooleanRef实例的字段。这个字段可以通过注释标记为volatile。你去了,@volatile毕竟在那里很有用!

回答你的第二个问题:Java的闭包只关闭了有效的最终值#34;这将禁止这种模式,其中doNotStop的值在闭包内发生变化。你当然可以像在这里一样使用&#34;有效的最终&#34;来实现它。对(Volatile)BooleanRef的引用,elem可以由闭包自由修改。