Today I Learned: Synchronized Blocks and Volatile Keyword

Picking right back off from where we left on Friday, Jakob was teaching us about the Java Memory Model. An understanding of it will be key to today’s discussion, so, if you need a refresher, check out my last post. We first explored the synchronized keyword in Java.

In previous discussions we learned what exactly a race condition was and what causes it. Now, using the synchronized keyword ensures that a block of code will not be accessed by multiple threads acting on the same object at the same time.

The synchronized keyword can be used to mark four different types of blocks:
1) Instance methods
2) Static methods
3) Code blocks inside instance methods
4) Code blocks inside static methods

Synchronized blocks are synchronized on the instance (object) owning the method. This is the main takeaway here. If a block is synchronized on an instance object, then two threads acting on that instance will not step on each other’s toes.

Technical jargon can be confusing so let’s look at a visual. Back to the skittles!

We have a pack of skittles. The pack has 100 skittles in it. The pack of skittles has an eat instance method on it and we have synchronized it.

Thread a: Receives the pack of skittles

Thread b: Receives the pack of skittles

Thread a: eats a skittle (Thread b must wait to eat! Thank’s synchronization.) and updates the pack’s count.

Thread b: want’s to eat at the same time as a but, realizes that Thread a has synchronized the eat method :(. Sorry Thread b. Wait your turn.

Thread a: finishes eating and allows Thread B to eat.

Race condition avoided! However, remember that the synchronization is on the object itself. So, if two packs of skittles were created the two threads could easily act on the different instances. The synchronization prevents two threads from acting on the same instance.

Secondly, we discussed the volatile keyword. This is where an understanding of the memory model comes in handy. Essentially, volatile ensures that a thread is reading/writing a variable from main memory and not from the CPU cache. As mentioned in the last article. Race conditions may occur because Thread A reads a variable into its CPU cache and does some processing on it, Thread B reads the same variable into its CPU cache and does some processing on it. Little does Thread B know, it has an out of date variable because Thread A never pushed the variable back to main memory. This is where volatile comes in handy.

The problem with threads not seeing the latest value of a variable because it has not yet been written back to main memory by another thread, is called a “visibility” problem. — Jakob

Another benefit of the volatile keyword is:

When a thread writes to a volatile variable, then not just the volatile variable itself is written to main memory. Also all other variables changed by the thread before writing to the volatile variable are also flushed to main memory. When a thread reads a volatile variable it will also read all other variables from main memory which were flushed to main memory together with the volatile variable.

We are basically retrieving a nice snapshot from main memory. This allows us to avoid any confusion caused by having out of date copies in the cache. Pretty neat stuff.

When to use each:

It is important to note that in the case where two threads are reading and writing to a shared variable, volatile is not enough. In this scenario, we should favor the synchronized keyword. Volatile can be favored when one thread reads and writes to a shared variable and multiple threads are reading the variable. Making the variable volatile ensures that the multiple threads read the most up to date value. — Very Important Stuff

Once again, I hope you enjoyed Jakob Jenkov’s articles as much as I did. Until tomorrow.

  • Jon

http://tutorials.jenkov.com/java-concurrency/synchronized.html

http://tutorials.jenkov.com/java-concurrency/volatile.html