Press "Enter" to skip to content

Synchronization in Java

Ajk 2

If you have followed my previous post – How to Implement Threads in Java, then you probably know by now how to create and start your own threads.

However what happens when 2 threads are trying to read/write the same variable/object? How do we go about successfully guaranteeing synchronization in Java and prevent a hazardous racing condition?

To illustrate quick problematic racing condition let’s take a look at the following example

What do you expect our main method to print? You probably expect it to print 0 twice, once for each thread that is going into the run() method. Well, turns out it sometimes prints -1, 0 and sometimes +1. Reason for this being that the two threads are sharing the variable fooledYaNumber. Hence the “seemingly” atomic expressions fooledYaNumber++ sometimes get executed in the wrong order. To further explain, (although it is assumed you know what a racing condition is by now), let me show you the following thread execution order, where count++ will be written as count = count + 1, which is compromised of two different computations, + and =:

  • T1 : count + 1 => 1
  • T1 : count = => 1
  • T2 : count + 1 => 2
  • T2 : count = => 2
  • T1 : count – 1 => 1
  • T2 : count – 1 => 1
  • T1 : count = => 1
  • T2 : count = => 1

As you can see the difference between the first and second part of the description above, since is no way of telling which thread will execute first. Hence our computations might get intertwined and as a result our method fail to do what we expect it to. There are quite a few ways to control thread execution in Java:

  • Collection classes such as ConcurrentHashMap, which we saw in our LRU Cache Implementation, provide a thread safe environment
  • Classes in java.util.concurrent.atomic package provide methods which are thread safe.
  • The volatile keyword will make the threads read data from memory and not the thread cache itself.
  • The java.util.concurrent.locks package provides different locks and condition which are very interesting and WILL BE explained better in my next post.
  • Synchronization in Java, which is probably the most used tool to provide thread safety and will be the main topic of this post.

Synchronization in Java

Yeah I know, you are probably thinking “finally”, but for things to be done right they have to be explained step by step and therefore the explanation of a racing condition and thread safety are very important concepts to grasp or refresh.

The synchronize keyword in Java is used to guarantee thread safety and only one executing thread inside the synchronized code. There are a lot of pointers we have to make about it why don’t you just write your first piece of synchronized code.

First off we can write synchronized methods which depend on the whole object:

The problem with a method like this is that it blocks the whole Object (Runnable in this case). Hence the other thread would be completely blocked and could not do some other business (such as doSomething(), which is completely unrelated to our indestructibleMethodButNotReally() method)!

Let’s try a new way and put synchronized in a block creating an Object variable for it to lock instead of the whole class object:

This way, our other threads can compute doSomething() while we are in our synchronized block and we minimize the time threads block each other to only when they need the same exact resources (i.e. cantFoolYouNow in this case). This is why you should always try to use the lower level of synchronization, to avoid blocking other threads as much as possible. Keep in mind that by Java specifications you cannot use the synchronized keyword on variables and constructors.

Synchronization in Java works by acquiring a lock on the Object in the block/method and providing exclusive access (meaning other threads cannot access the locked Object and have to wait). In the case of methods it blocks the whole object, which is the same as doing a synchronized(this) block, in the case of static methods it blocks the Class Object, which is different. Hence do not mix and match static and non-static synchronized methods.

A few pitfalls:

  • You cannot synchronize a non-initialized/null Object. You will get a NullPointerException
  • Locking on implicitly constructed primitive Objects such as Strings will prevent these Objects from being used anywhere. So just Don’t Sychronize on Strings.
  • Using the Synchronization keyword in Java will result in a bit of performance loss
  • You should be careful on preventing DeadLock when nesting synchronizations (will be explained better in my next post), but in cheap words consider:
    – Thread 1: Has Lock 1 (into synchronized(lock1) block waits for Lock 2 (nested synchronized(lock2)
    – Thread 2: Has Lock 2 (into synchronized(lock2) block waits for Lock 1 (nested synchronized(lock1)
    What happens in this case? The answer is easy – DeadLocK! None can complete. You might as well take a vacation cuz none of your work will complete!
  • Finally, be very careful about your “locking” Objects. They should not be available outside of the scope of your synchronized part. Otherwise your system might be easily targeted by a DoS (Denial of Service) attack. For example: