Hi! Today I’d like to write a small note/reminder about a subtle and unintended threads synchronization “feature” by using a shared object, for instance logger. The example will be in Java, and you can check it out with your favorite language (not to say that Java is mine).
To get started, you can
git clone https://github.com/pluton8/java_concurrency_examples.git to get the described classes. We’re interested in the
e202_sequence_notsynchronizedlist_logging classes. As the doc states, it is
A very contrived yet simple example of a Sequence class producing increasing natural numbers with each nextNumber() call. Two Worker threads get the numbers, sharing the same instance, and add them to a shared List. After they’ve added a certain number of numbers, the main object checks if all the numbers in the list are increasing uniformly.
When you run the first version you can get such an output:
or even more interesting one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
In any case, you can notice a number of
nulls in the numbers list. Where did they come from? Our
nextNumber() method can only produce incrementing numbers. Let’s check the return values of the method in the console by adding the simple logging line here (line 36):
1 2 3
The result is class
e202_sequence_notsynchronizedlist_logging. No way! I run this version a few dozen of times and never got a
null in the list:
What happened? It’s this phenomenon when using a shared object, the console output stream in this case, implicitly creates a lock and gets rid of the synchronization issue. However, you should never use such a technique, because it’s not obvious and logging could be on in
debug (no runtime issues) and off in
release (users will face issues).
Remember about it and don’t fall for it. Concurrency is not an easy topic, and the only way to master it is practice.