Локальная переменная может быть изменена из дочерних потоков в Scala - PullRequest
1 голос
/ 09 июля 2020

Сегодня я пытался узнать об управлении памятью в JVM и наткнулся на следующий вопрос: могу ли я изменить локальную переменную из двух потоков, порожденных одной и той же функцией?

В Java, если вы попробуете что-то вроде этого, код не будет компилироваться, что приведет к ошибке с сообщением "локальные переменные, на которые ссылается внутренний класс, должны быть окончательными или фактически окончательными"

public class MyClass {
  static void f() throws Exception {
    int x = 0;
    
    Thread t1 = new Thread(new Runnable() {
      public void run() {
        for(int i = 0; i < 1000; i++) {
          x = x + 1;   
        }
      }
    });
     
    Thread t2 = new Thread(new Runnable() {
      public void run() {
        for(int i = 0; i < 1000; i++) {
          x = x - 1;   
        }
      }
    });
      
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(x);
  }
  public static void main(String args[]) throws Exception {
    for(int i = 0; i < 20; i++) {
      f();    
    }
  }
}

Однако эквивалентный код в Scala компилируется и работать без проблем (несмотря, возможно, на состояние гонки):

def f(): Unit = {
  var x = 0
  
  val t1 = new Thread(new Runnable {
    override def run(): Unit =
      (1 to 1000).foreach(_ => {x = x + 1})
  })
  
  t1.start()
  val t2 = new Thread(new Runnable {
    override def run(): Unit =
      (1 to 1000).foreach(_ => {x = x - 1})
  })
  t2.start()
  t1.join()
  t2.join()
  println(x)
}

(1 to 20).foreach(_ => f())

Почему поведение в каждом случае разное?

1 Ответ

3 голосов
/ 09 июля 2020

В лямбдах Scala и, как следствие, анонимные классы могут захватывать локальные переменные. Пакет scala.runtime содержит несколько дополнительных классов для этой цели. Они эффективно переводят локальную переменную в переменную экземпляра другого класса, экземпляры которого могут использоваться совместно: https://github.com/scala/scala/blob/v2.13.3/src/library/scala/runtime/ObjectRef.java

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...