It is easy to understand and expect the output when there is only a single thread running in Java application. You are able to process code sequentially and expect the output. Things starts getting tricky when two or more threads are working at same time.

Check out the following example, can you figure out what can go wrong in process of spending money. 2 different threads are checking the balance in the same account and trying to withdraw amount at same time. It could happen that both thread check if balance is greater than the amount to be spent and amount will get overdrawn.

package com.shrikane.concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrencyInJava {

    public static void main(String[] args) {
        final BankAccount bankAccount = new BankAccount(100);
        final WithdrawMoney withdrawMoney1 = new WithdrawMoney(bankAccount, 10);
        final WithdrawMoney withdrawMoney2 = new WithdrawMoney(bankAccount, 100);

        final ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(withdrawMoney1);
        executorService.execute(withdrawMoney2);

        executorService.shutdown();
    }

    static class WithdrawMoney implements Runnable {

        private final BankAccount bankAccount;
        private final int amountToWithdraw;

        WithdrawMoney(
                final BankAccount bankAccount,
                final int amountToWithdraw) {
            this.bankAccount = bankAccount;
            this.amountToWithdraw = amountToWithdraw;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            if (bankAccount.balance > amountToWithdraw) {
                System.out.println(String.format("Withdraw Successful %s", threadName));
                bankAccount.spendAmount(amountToWithdraw);
            } else {
                System.out.println(String.format("Withdraw Failed %s", threadName));
            }
        }
    }

    static class BankAccount {
        private int balance;

        BankAccount(int balance) {
            this.balance = balance;
        }

        public int getBalance() {
            return balance;
        }

        public void spendAmount(int amountToSpend) {
            balance = balance - amountToSpend;
            if (balance < 0) {
                System.out.println(String.format("Amount Overdrawn %s", Thread.currentThread().getName()));
            }
        }
    }
}
So, the problematic output looks like
Withdraw Successful pool-1-thread-1
Withdraw Successful pool-1-thread-2
Overdrawn

So, let’s see how this problem can be solved. we need to acquire lock before this 2-part transaction takes place.

  1. Checking if account has sufficient balance
  2. And then withdrawing the money.

And we use the word “synchronized” to solve the problem.

 @Override
    public void run() {
        synchronized (bankAccount) {
            String threadName = Thread.currentThread().getName();
            if (bankAccount.balance > amountToWithdraw) {
                System.out.println(String.format("Withdraw Successful %s", threadName));
                bankAccount.spendAmount(amountToWithdraw);
            } else {
                System.out.println(String.format("Withdraw Failed %s", threadName));
            }
        }
    }
New correct output looks like
Withdraw Successful pool-1-thread-1
Withdraw Failed pool-1-thread-2

One reply on “Concurrency in Java explained”

  1. A WordPress Commenter

    Reply

    Hi, this is a comment.
    To get started with moderating, editing, and deleting comments, please visit the Comments screen in the dashboard.
    Commenter avatars come from Gravatar.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.