Java synchronized keyword example

The java synchronized keyword allows only one thread to enter a method or a block of code. It is the quickest and most basic way of preventing data corruption caused by multiple threads running concurrently.

Why use synchronized keyword?

Multiple threads can access the same object or the same database connection at the same time.
Let’s consider the following example of an ATM machine that withdraws or deposits money to a bank account.

BankAccount.java class:

package net.superglobals.tutorials;

public class BankAccount {
	private String IBAN;
	private double balance;
	
	public BankAccount(String IBAN, double balance) {
		this.IBAN = IBAN;
		this.balance = balance;
	}
	
	public void withdraw(double amount) {
		balance = balance - amount;
	}
	
	public void deposit(double amount) {
		balance = balance + amount;
	}
	
	public double getBalance() {
		return balance;
	}
	
	public void setBalance(double balance) {
		this.balance = balance;
	}
	
	public String getIBAN() {
		return IBAN;
	}
	
	public void setIBAN(String iBAN) {
		IBAN = iBAN;
	}
}

Main.java class. It has two threads that operate on the same BankAccount object.

  • In the first Runnable we withdraw 100$ a thousand times.
  • In the second Runnable we deposit 100$ one thousand times.
  • In the end we should have the original balance, 1000$.
package net.superglobals.tutorials;

public class Main {
	public static void main(String[] args) throws InterruptedException {
		BankAccount bankAccount = new BankAccount("ENAMN154778888", 1000);
		
                //thread using lambda
		Thread t1 = new Thread(() -> {
			for (int i = 0; i < 1000; i++) {
				bankAccount.withdraw(100);
			}
		});

		//thread using Runnable
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					bankAccount.deposit(100);
				}
			}
		});
		
		t1.start();
		t2.start();

		//wait for both threads to finish before displaying the balance
		t1.join();
		t2.join();
		System.out.println(bankAccount.getBalance());
	} 
}

But running this class multiple times will show different balance values. This is because threads access very fast the same resource and data corruption occurs.
Consider that balance = 1000.

  1. #Thread1 enters and withdraws. It just calculates balance – 100 = 900 but doesn’t assign the result because it is put to sleep by the OS.
  2. #Thread2 enters and reads balance = 1000 (remember the result of the last operation wasn’t assigned).
  3. #Thread2 adds 100 to 1000 and assigns balance = 1100 (instead of 1000).
  4. #Thread2 wakes up again and finishes the assignment: balance = 900. It should have been 1000 (we subtract 100 then add 100 again). Data corruption occurred.

To solve this add the synchronized keyword to the methods withdraw(double amount) and subtract(double amount).

public synchronized void withdraw(double amount) {
		balance = balance - amount;
	}
	
	public synchronized void deposit(double amount) {
		balance = balance + amount;
	}

Now every time you run the example it will show 1000.0 as the balance. Which is true because we start with 1000, we subtract 1000 x 100 then add 1000 x 100.
Adding and subtracting is done concurrently not one after another) so we should end up with the same amount. True.

You can also protected a block of code so that only one thread at a time will enter by wrapping it like this:

synchronized (this) {
   int a = 3;  
   int b = 4;
   int c = b - a;
}
FacebookTwitterLinkedin