Reputation: 362
Today I was trying to learn about memory management in the JVM and I came across the following question: can I mutate a local variable from two threads spawned within the same function?
In Java, if you try something like this the code will not compile, yielding an error with message "local variables referenced from an inner class must be final or effectively final"
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();
}
}
}
However the equivalent code in Scala does compile and run without problem (despite maybe race conditions):
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())
Why is the behavior different in each case?
Upvotes: 1
Views: 194
Reputation: 15086
In Scala lambdas, and by extension anonymous classes, can capture local variables. The scala.runtime
package contains some extra classes for that purpose. They effectively lift the local variable into an instance variable of another class whose instances can be shared: https://github.com/scala/scala/blob/v2.13.3/src/library/scala/runtime/ObjectRef.java
Upvotes: 4