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

ron*_*ron 8 concurrency scala immutability

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

Pao*_*lla 12

从多线程的角度来看,对val本身的赋值很好,因为你在声明它时必须给val赋值,并且以后不能改变这个值(所以如果你这样做val s="hello",s是"你好" "从诞生之日起:没有线程会读到另一个值.但是有一些警告:

1 - 如果将可变类的实例分配给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
Run Code Online (Sandbox Code Playgroud)

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

import java.lang.reflect.Field
class Foo { val foo=true } // foo is immutable

object test {
  def main(args: Array[String]) {  
        val f = new Foo
        println("foo is " + f.foo) // "foo is true"

        val fld = f.getClass.getDeclaredField("foo")
        fld.setAccessible(true) 
        fld.setBoolean(f, false) 
        println("foo is " + f.foo) // "foo is false"
  }
}
Run Code Online (Sandbox Code Playgroud)


axe*_*l22 9

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

object Obj {
  val r = 1

  def foo {
    val a = 1
    def bar = a
    bar
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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