Skip to content

Why don’t instance fields need to be final or effectively final to be used in lambda expressions?

I’m practicing lambda expressions in Java. I know local variables need to be final or effectively final according to the Oracle documentation for Java SE 16 Lambda Body :

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be final or effectively final (§4.12.4), as specified in §6.5.6.1.

It doesn’t say why though. Searching I found this similar question Why do variables in lambdas have to be final or effectively final?, where StackOverflow user “snr” responded with the next quote:

Local variables in Java have until now been immune to race conditions and visibility problems because they are accessible only to the thread executing the method in which they are declared. But a lambda can be passed from the thread that created it to a different thread, and that immunity would therefore be lost if the lambda, evaluated by the second thread, were given the ability to mutate local variables.

This is what I understand: a method can only be executed by one thread (let’s say thread_1) at a time. This ensures the local variables of that particular method are modified only by thread_1. On the other hand, a lambda can be passed to a different thread (thread_2), so… if thread_1 finishes with the lambda expression and keeps executing the rest of the method it could change the values of the local variables, and, at the same time, thread_2 could be changing the same variables within the lambda expression. Then, that’s why this restriction exists (local variables need to be final or effectively final).

Sorry for the long explanation. Am I getting this right?

But the next questions would be:

  • Why isn’t this case applicable to instance variables?
  • What could happen if thread_1 changes instance variables at the same time as thread_2 (even if they are not executing a lambda expression)?
  • Are instance variables protected in another way?

I don’t have much experience with Java. Sorry if my questions have obvious answers.

Answer

Instance variables are stored in the heap space whereas local variables are stored in the stack space. Each thread maintains its own stack and hence the local variables are not shared across the threads. On the other hand, the heap space is shared by all threads and therefore, multiple threads can modify an instance variable. There are various mechanisms to make the data thread-safe and you can find many related discussions on this platform. Just for the sake of completeness, I’ve quoted below an excerpt from http://web.mit.edu/6.005/www/fa14/classes/18-thread-safety/

There are basically four ways to make variable access safe in
shared-memory concurrency:

  • Confinement. Don’t share the variable between threads. This idea is called confinement, and we’ll explore it today.
  • Immutability. Make the shared data immutable. We’ve talked a lot about immutability already, but there are some additional constraints
    for concurrent programming that we’ll talk about in this reading.
  • Threadsafe data type. Encapsulate the shared data in an existing threadsafe data type that does the coordination for you. We’ll talk
    about that today.
  • Synchronization. Use synchronization to keep the threads from accessing the variable at the same time. Synchronization is what you
    need to build your own threadsafe data type.