Abstract: In this fifth law of concurrency, we look at a deadly law where a field value is written early.
Welcome to the 151st issue of The Java(tm) Specialists' Newsletter, sent to you from the Island of Crete somewhere in the Mediterranean. I am sitting inside writing this newsletter, so no beach stories today. Outside is a chilly 24 Celsius / 75 Fahrenheit.
This coming Saturday (6th October 2007) I am presenting a talk on the Secrets of Concurrency at the Java Hellenic User Group in Athens. Next week on Thursday (11th October 2007), I am presenting the same talk at the JFall 07 conference in Bussum, Netherlands. Come say "hi" if you are at either of these events :-)
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
We are looking at a series of laws to make sense of Java concurrency. Just to remind you, here they are again. In this newsletter, we will look at the Law of the Leaked Memo.
Imagine for a moment that you are the director of a listed company. You are going to announce tomorrow that 50% of the staff is being retrenched. You have secretly typed the memo on your own laptop and personally made 500 photocopies. The reason why this information must not be leaked is because it will undoubtedly affect the share price, allowing someone with insider knowledge to make a quick buck and landing you in jail. However, after you make the 500 copies, you forget to remove the original memo from the photocopy machine. The secretary thread picks up the memo accidentally and soon there is widespread panic in the company.
It can happen in Java that without proper synchronization, a field value might be leaked before it should. The Java Memory Model gives the implementors the abiliy to reorder statements. To the Java thread, everything will appear to be in the same order, but other threads might see your early writes.
Let's look at an example:
public class EarlyWrites { private int x; private int y; public void f() { int a = x; y = 3; } public void g() { int b = y; x = 4; } }
If two threads call f() and g(), what are the possible values
for a and b at the end of the methods? The obvious answers
are a=4,b=0
and a=0,b=3
. Another
fairly obvious answer is a=0,b=0
if the scheduler
swaps between the threads after the first line of either method
was executed.
However, there is a fourth possibility. Both threads might reorder the statements, resulting in:
public void f() { y = 3; int a = x; } public void g() { x = 4; int b = y; }
This can result in a=4,b=3
.
This reordering can happen at most places of the program. There are some restrictions though. For example, if you have a synchronized block, the hotspot compiler can reorder statements inside the block and it can move statements that lie outside the synchronized block into the block. However, it may not move code that is inside a synchronized block to the outside. We can use this restriction to prevent early writes from happening in critical sections of our code.
Another way of preventing early writes is to mark the field
as volatile
. We read in the Java
Memory Model:
There is a happens-before edge from a write to a volatile variable v to all subsequent reads of v by any thread (where subsequent is defined according to the synchronization order)
This early writing is one of the reasons why the double-checked locking is broken in Java. The value of the field might get assigned before the constructor is called. Thus you might receive an uninitialised instance. A fix is to make the instance field volatile.
This law takes a bit of time to get your mind around. It is able to refute a lot of "clever" code that people have written to avoid synchronization.
Kind regards from Greece
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.