Overview
Recently we came across a need to use volatile
. At the time my spider senses were tingling; I wasn’t sure. We confirmed that it was in fact necessary. Then we improved the implementation a touch, which removed the need to use the keyword.
The Beginning
I joined a mob while it was in the middle of updating some inherited multi-threaded code. More on that in a forthcoming blog, but in summary:
- One thread existed to capture log output from an application running in the cloud.
- Another thread waited on values in that log. It started everything, waited for the system under test to handle a message, publish a message, and then waited for a result.
- The group I was with didn’t write the original solution, so while there were some automated tests, we were back-filling missing tests.
As I looked at the code collecting the log lines, it looked something like:
This code is inefficient, to be sure, but when I saw that, and realized that another thread was looking at the collected value, I was pretty certain that we needed the mark the field as volatile:
This lucky intuition came about because I read Concurrent Programming in Java back when it was published.
We got the test passing and then “verified” that volatile
was needed by seeing the test fail without it and pass with it.
Time, as usual, to the rescue
Later that evening I realized that the hunch I had was nearly correct. I was thinking in terms of values rather than refernces. String
values cannot change. In this particular case the issue was a reference changing versus its value because its value could not change.
By Reference, By Value
My “main” language is C. I understand it the best. To understand this need for volatile, I think it helps to understand pass by value.
In C
Everything is pass by value. If you want the equivalent of pass by reference, pass the value of the address of the thing. A pointer. The underlying mechanism is still by value, but the value of the value is quite a bit different.
In Java
In Java, it’s the same thing. But when you pass a non-primitive (instance of a class), you pass a reference to the object. If you pass a primitive, just like a reference, the value is copied. Unlike C, in Java you won’t be passing the address of a primitive.
What does that have to do with anything?!
If two threads are looking at the same field and one changes it outside of a synchronized block, the other might not see the change.
Not all changes are equal, however. “Changing” a String means getting another one altogether. That is, to get a new value of a String, the reference must change to point to a new location in memory.
Using volatile tells the underlying executing code to re-read the current value of the field (a reference to a String) each time as it might have changed.
Whew! That’s all over.
The volatile
keyword informs java to not assume values are fixed. The thread waiting for the value, checking it every so often, cached the value of the reference the first time it ran, but never checked the value of the reference again. The other thread changes the value of the reference every chance it gets:
At Time T1, the LogGobbler
points to an empty string. As the main thread starts, it picks up that reference, and caches it.
Also at T1, the gobbler thread starts and points to the exact same object (a String
in memory).
The main thread checks the log values, waiting for a signal to that it is OK to continue. However, since the thread assumes the underlying value of the field isn’t changing, it keeps checking the original value.
Once logs start flowing, the gobbler thread picks up all the available lines and appends them to its internal field. Since strings are immutable, the code actually creates a new string and sets the field to the new string.
At Time T2, the main thread isn’t paying attention because it has cached a reference to the original, empty, string.
At Time T3 and going forward, nothing really changes.
So as written, the code requires volatile
.
But do it better
String concatenation is inefficient. In this particular case efficiency doesn’t matter. However, in addition to being inefficient, it makes things like threading a bit more of an issue.
Collecting strings in a StringBuilder is a better general solution. As it turns out, it also resolves the need for the volatile
keyword.
Now the field looks like this:
Since each thread uses the same reference, and also, since that reference does not change, there’s no need to mark this reference as volatile
.
Now both threads look at the same object, a StringBuilder, whose reference never changes, but its internals change as one thread appends logs to it.
Conclusion
I was pleasantly surprised that I guessed right for the need to use volatile
. While I wasn’t spot on, I was close enough to be dangerous.
Improving the implementation also and simplified the solution and removed the need for volatile
altogether.
In this case, a String is more like a primitive than a object. Something needs to change (we’re collecting information). A String (the collector) is immutable. Thus, create a new string to have an updated value. In functional programming, this is a blessing. In stateful OO programming, it’s an unfortunate characteristics (context and all that).
The StringBuffer is a better implementation and it somewhat moves away from border-line primitive obsession. I’d even go a bit further and say that this is a micro version of the dependency inversion principle as well, but that’s for as as yet even later blog, or maybe an earlier one.