Scala val должен быть защищен синхронизированными для одновременного доступа? - PullRequest
8 голосов
/ 16 марта 2011

Как я читал, Scala неизменный val не переводится на Java final по разным причинам. Означает ли это, что доступ к val из другого потока должен быть защищен синхронизацией, чтобы гарантировать видимость?

Ответы [ 2 ]

12 голосов
/ 16 марта 2011

присваивание val само по себе хорошо с точки зрения многопоточности, потому что вы должны присваивать значению val при его объявлении, и это значение не может быть изменено в будущем (поэтому, если вы сделаете val s="hello", s "привет" с самого рождения: ни один поток не будет читать другое значение).Однако есть несколько предостережений:

1 - если вы присваиваете экземпляру изменяемого класса значение val, само по себе val не будет "защищать" внутреннее состояние класса отchange.

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

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"
  }
}
9 голосов
/ 16 марта 2011

Как члены объекта, после инициализации, val s никогда не изменяют свои значения в течение времени жизни объекта. Таким образом, их значения гарантированно будут видны всем потокам, при условии, что ссылка на объект не скрылась в конструкторе. И фактически они получают модификаторы 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 фактически генерирует получатель с тем же именем и частным полем, к которому получает доступ получатель. Попытка использовать отражение для изменения поля приведет к NoSuchFieldException. Единственный способ изменить его - добавить аннотацию specialized в ваш класс, которая сделает специальные поля защищенными и, следовательно, доступными для отражения. В настоящее время я не могу думать ни о какой другой ситуации, которая могла бы изменить что-то, объявленное как val ...

...