Immutability = Thread-safety
No, there is not a typo in the title of this post. It is a distorted quotation from the Slipknot song “People equal s**t”
And more or less this was my reaction when I discovered that in Java immutability is not equals to thread-safety. Not always. Let me explain why.
We need some definition first
All it comes from the fact that I am reading the book Java concurrency in practice, a.k.a. JCIP, written by Brian Goetz. The book is very well written and it is a milestone in the Java Community. In the book, Goetz and al. tries to give us a reasoned vision of the problems belonging to the concurrency world. The language used in the examples is obviously Java, but this is a collateral fact.
So, we are trying to answer to the question “Does immutability imply thread-safety?”. First, we need to give the definition of thread-safety and immutability. As Goetz wrote in JCIP, it is very difficult to give a definition to the concept of thread-safety. Basically,
Writing thread-safe code is, at its core, about managing access to state, and in particular to shared, mutable state. [..] An object’s state is its data, stored in state variables such as instance or static fields. [..] By shared, we mean that a variable could be accessed by multiple threads; by mutable, we mean that its value could change during its lifetime. [..] Whether an object needs to be thread-safe depends on whether it will be accessed from multiple threads. This is a property of how the object is used in a program, not what it does.
Dafuq! So, speaking about thread-safety does not mean to speak about the execution of the program. The main focus is on the state of the classes that compose a program, and on how this state may be accessed by other threads.
Talking about an object state, let’s introduce the definition of immutability. We can take the definition from item 15, Minimize mutability of the book Effective Java by Joshua Bloch:
An immutable class is simply a class whose instances cannot be modified. All of the information contained in each instance is provided when it is created and is fixed for the lifetime of the object.
These two definitions let you glimpse the relationship between immutability and thread-safety. If thread-safety is related to the possible changes of objects’ internal state, avoiding those changes makes easy to talk about thread-safety.
Ok, this is the theory and we like it. But, we want to make our hands dirty. So, let’s talk about code.
Immutability in Java
It is surprising that there is no an idiomatic way in the Java language to make a class immutable. I suppose that the problem here is that Java winks to C++ and other procedural programming languages, more than to Lisp and Haskell. By the way, we can try to implement an immutable class strictly following the above definition.
As requested by definition, the state of class
Person is not modifiable after the creation of an object. Every
attribute of the class is also immutable, so there is no possibility that a mutable state can “escapes” from the class
using the accessor methods. Nice. The only problem is the Java Memory Model of the JVM that executes your code, but who
The Java Memory Model
Seriously, there is a problem with the above code. First of all: what is the Java Memory Model, a.k.a. JMM or JSR133? Looking at the FAQ page of the JSR133 we can find that
The Java Memory Model describes what behaviors are legal in multithreaded code, and how threads may interact through memory. It describes the relationship between variables in a program and the low-level details of storing and retrieving them to and from memory or registers in a real computer system. It does this in a way that can be implemented correctly using a wide variety of hardware and a wide variety of compiler optimizations.
The JMM that we are using was released together with the version 5 of Java. Prior to this, the former JMM was so full of bugs that even the “Double check locking” technique did not work properly.
Probably you’re asking yourself why the hell you have to be worried about the JMM. The problem is that the statements defined in a constructor method could be reordered by the compiler, as well as the ones of any other method. Take for example the following code taken from JCIP.
We know for sure that this class is not thread-safe because the attribute
resource could be instantiated more then
once (see The Good, the Bad and the Singleton
for details). But, what is surprising is that
getInstancecould return an object in an inconsistent state
Why should this happen? Because of compiler reordering. Without any synchronization mechanism, the above statement 2 is not atomic for the JVM and so it could be completely rewritten by the compiler. For this reason, the execution could lead to this order of statements:
- allocate some memory
- create the new object
- initialise its fields with their default value (false for
0for other primitives,
- assign the reference to the newly constructed object to resource
- run the constructor, which includes running parent constructors too
As you have guessed, steps 4 and 5 are executed in a more then insane order, and this leads clearly to
thread-safety problems. What is more surprising is that the
getInstance could return
null. However, the proof of this
statement is very hard, so I recommend you to read it from a post of the user assylias,
Java Memory Model and reordering.
So, are we fucked up!? No, we’re not and the solution is easier than what you’re thinking about.
Fortunately, Goetz and al. put a strong requirement in the definition of the JSR133, which is
Assuming the object is constructed “correctly”, once an object is constructed, the values assigned to the
finalfields in the constructor will be visible to all other threads without synchronization. In addition, the visible values for any other object or array referenced by those
finalfields will be at least as up-to-date as the final fields.
Informally, the above sentences mean that we have to declare the attributes of a class as
final, if we want to prevent that
their construction will be reordered by the compiler. And this fact closes the cycle. Recalling the definition of
immutability which we gave above, we can now assert that the right way to implement the immutable
Person class is:
As you can imagine, it is important to use
final attributes as much as we can. In this way, we can save a lot of time
from debugging multi-threaded code, that is not working properly.
Finally, under these conditions we can now proudly say that: “Yes, Immutability implies thread-safety”.
Immutability in Scala
Scala and many other modern programming languages are build on top of the JVM. For this reason, they do not define a custom model for multi-threaded programs execution, but they reuse the one defined in Java. A question easily rises: do Scala suffer of the same problems we have seen so far? I’ve made this question on Stackoverflow. Nobody answer my question, so I found the answer from myself :(
One of the cool things about Scala is that it was born years after Java. So, Martin Odersky and his colleagues had the
opportunity to learn from early Java’s design errors. They understood that immutability is so important as a concept,
that they introduced an idiomatic way to declare a class as immutable, using a
case class. For example, our
Person in Scala will become the following.
The question now is: when the Scala compiler generates the
.class bytecode file, does it implement the two immutable
attributes using the
final keyword? It seems that the only way to know if this fact is true, is to use the java
javap and look at the information which it extracts.
So, I’ve compiled the following class with the
scalac compiler, version 2.10.2.
And then I’ve run the command
javap -p A.class on the
.class file that was generated by the compiler. The output
of the execution was the following disassembled code
As you can see from the above code snippet, the sole attribute
a was translated by
We can conclude that in Scala the concept of immutability is idiomatically implemented in a way that is consistent with respect to the JMM. In Scala it is true to say that immutability implies thread-safety.
- Chapter II: Thread Safety. Java concurrency in practice, Brian Goetz, 2006, Addison-Wesley Professional
- Item 15: Minimize mutability. Effective Java, Joshua Bloch, 2008, Addison-Wesley
- What is a memory model, anyway?
- How do final fields work under the new JMM?
- Double check locking
- Java Memory Model and reordering