Provide an example of threading and synchronization in Java

The best way to really understand threading and the need for synchronization is through a great example. Here we will present an example of an online banking system to really help see the potential problems with multi-threading, and their solutions through the use of a thread synchronization construct like a monitor.

Let’s suppose we have an online banking system, where people can log in and access their account information. Whenever someone logs in to their account online, they receive a separate and unique thread so that different bank account holders can access the central system simultaneously.

Now let’s create a Java class to represent those individual bank accounts. Instances of this class are created when people actually log in online. Let’s name the class BankAccount. This class has a method called “deposit” that’s used to deposit funds into the bank account. This class also has another method called “transfer” to transfer funds from the bank to another account.

Here is some simple Java code that represents the BankAccount class:

public class BankAccount {

    int accountNumber; 
	
    double accountBalance;


    // to withdraw funds from the account
    public boolean transfer (double amount)  
    {
        double newAccountBalance;

        if( amount > accountBalance)
	{
             //there are not enough funds in the account
             return false; 
	}
		
	else
	{
            newAccountBalance = accountBalance - amount;
            accountBalance = newAccountBalance;
            return true;
        }

    }

    public boolean deposit(double amount) 
    {
	double newAccountBalance;

	if( amount < 0.0)
	{
          return false; // can not deposit a negative amount
	}
		
	else
	{
          newAccountBalance = accountBalance + amount;
          accountBalance = newAccountBalance;
	  return true;
         }

     }


Example of a race condition in Java

You've now seen the code above, but let's get into the problems that we can run into when we have a multi-threaded application. The problem that we will be presenting below is what's called a race condition. A race condition occurs when a program or application malfunctions because of an unexpected ordering of events that produces contention over the same resource. That sounds confusing, but it will make a lot more sense once you read the example below.

So, let’s get into the actual problem. Let’s say that there’s a husband and wife - Jack and Jill - who share a joint account. They currently have $1,000 in their account. They both log in to their online bank account at the same time, but from different locations.

They both decide to deposit $200 each into their account through a wire transfer from other bank accounts that they have at the same time. So, the total account balance after these 2 deposits should be $1,000 + ($200 * 2), which equals $1,400.

Let’s say Jill’s transaction goes through first, but Jill's thread of execution is switched out (to Jack’s transaction thread) right after executing this line of code in the deposit method:


newAccountBalance = accountBalance + amount;

Now, the processor is running the thread for Jack, who is also depositing $200 into their account. When Jack’s thread deposits $200, the account balance is still only $1,000, because the variable accountBalance has not yet been updated in Jill’s thread. Remember that Jill’s thread stopped execution right before the accountBalance variable was updated.

So, Jack’s thread runs until it completes the deposit function, and then updates the value of the accountBalance variable to $1200. After this, control returns to Jill’s thread, where newAccountBalance has the value of $1200. Then, it just assigns this value of $1,200 to accountBalance and returns. And that is the end of execution.

What is the result of these 2 deposits of $200? Well, the accountBalance variable ends up being set to only $1200, when it should have been $1400. This means Jack and Jill lost $200. This is good for the bank, but a huge problem for Jack and Jill, and any other of the bank's customers.

The cause of the race condition

So, do you see how the problem was caused here? Because Jill’s thread switched out (to Jack’s thread) right before the accountBalance variable was updated, Jill’s deposit was not counted.

If you remember the definition of a race condition, the example we just gave should clear it up. Here's the definition of a race condition again, in case you forgot: A race condition occurs when a program or application malfunctions because of an unexpected ordering of events that produces contention over the same resource. Hopefully, now it makes a lot more sense.

Synchronization fixes race conditions in multi-threaded programs

But the real question is how can this problem be fixed? Well, it should be clear that the code needs to allow the deposit function to run to completion without switching to run a different thread. This is what synchronization is all about - fixing issues like this! This can be accomplished with a synchronization construct like a monitor.

Example of using the synchronized keyword in Java

This problem is easily fixed in Java. In the code below, all we do is add the synchronized keyword to the transfer and deposit methods to create a monitor. If you need a refresher on monitors then you can read this article: Monitors vs semaphores.

public class BankAccount {

    int accountNumber; 
	
    double accountBalance;


    // to withdraw funds from the account
    public synchronized boolean transfer (double amount)  
    {
        double newAccountBalance;

        if( amount > accountBalance)
	{
             //there are not enough funds in the account
             return false; 
	}
		
	else
	{
            newAccountBalance = accountBalance - amount;
            accountBalance = newAccountBalance;
            return true;
        }

    }

    public synchronized boolean deposit(double amount) 
    {
	double newAccountBalance;

	if( amount < 0.0)
	{
          return false; // can not deposit a negative amount
	}
		
	else
	{
          newAccountBalance = accountBalance + amount;
          accountBalance = newAccountBalance;
	  return true;
         }

     }


Synchronized keyword locks methods

What does the synchronized keyword do for us here? Well, if a thread is executing inside either the deposit or transfer blocks, then it is now impossible for any other threads to enter either of those methods. This means that only one thread can execute those functions at a time - which is exactly what we want to prevent the problem with the accountBalance variable that we described earlier.

First, it is not possible for two invocations of synchronized methods on the same object to interleave - so one thread can not interrupt another thread until it is done executing all of the code in a synchronized method. So, when one thread is executing a synchronized method all other threads are blocked from entering that method.


Hiring? Job Hunting? Post a JOB or your RESUME on our JOB BOARD >>

Subscribe to our newsletter for more free interview questions.



FOLLOW Varoon Sahgal, Author of ProgrammerInterviewon

  • Anil

    Nice post

  • Gaurav Gupta

    Nice post..thanku.. :)

  • Gurucharan Vijayakumar

    Thanks, Its easy to understand with the real time problems. :)

  • Deepak

    Thanks. useful post…:)

  • parita

    Very good explanation and thanx

  • mayya

    thanks . .good example.

  • shabbir

    Very nice explanation… Thanks a lot…

  • Chuc

    great post. Thanks.