Scala val必须保持同步访问的同步?

时间:2011-03-16 14:59:40

标签: scala concurrency immutability

正如我所读到的,Scala不可变val由于各种原因未被转换为Java final。这是否意味着必须保护从其他线程访问val以保证同步,以保证可见性?

2 个答案:

答案 0 :(得分:12)

从多线程的角度来看,对val本身的赋值很好,因为在声明它时你必须为val赋值,并且以后不能改变该值(所以如果你做了{{ 1}},s从诞生开始是“你好”:没有线程会读取另一个值)。 然而,有几点需要注意:

1 - 如果您将可变类的实例分配给val,则val本身不会“保护”该类的内部状态不会更改。

val s="hello"

2 - 您(或您的某个库...)可以通过反射更改val。如果发生这种情况,两个线程确实可以为您的val

读取不同的值
class Foo(s:String) { var thisIsMutable=s }
// you can then do this
val x = new Foo("hello")
x.thisIsMutable="goodbye"
// note that val guarantees that x is still the same instance of Foo
// reassigning x = new Foo("goodbye") would be illegal

答案 1 :(得分:9)

作为对象成员,初始化后,val永远不会在对象的生命周期内更改其值。因此,只要对象的引用没有在构造函数中转义,所以保证它们的值对所有线程都可见。事实上,他们获得了Java final修饰符,如下所示:

object Obj {
  val r = 1

  def foo {
    val a = 1
    def bar = a
    bar
  }
}

使用javap:

...
private final int r;
...

public void foo();
...
   0:   iconst_1
   1:   istore_1
   2:   aload_0
   3:   iload_1
   4:   invokespecial   #31; //Method bar$1:(I)I
   7:   pop
...
private final int bar$1(int);
...
   0:   iload_1
   1:   ireturn
...

作为方法本地,它们仅在方法中使用,或者它们作为参数传递给嵌套方法或闭包(参见上面提到的bar$1)。闭包可能会传递给另一个线程,但它只有一个最终字段,其值为本地val。因此,它们从创建它们到所有其他线程的位置都是可见的,并且不需要同步。

请注意,这并未说明val指向的对象 - 它本身可能是可变的并且需要同步。

在大多数情况下,不能通过反射来违反上述内容 - Scala val成员声明实际上会生成一个具有相同名称的getter和一个getter访问的私有字段。尝试使用反射来修改字段将导致NoSuchFieldException。您可以修改它的唯一方法是在类中添加specialized注释,这将使专用字段受到保护,从而可以进行反射。我目前无法想到任何其他情况可能会改变声明为val ...

的内容