1. We use ReentrantLock for locking the Resource(totalSeats)
  2. Incase anything goes wrong (Exception being thrown etc.) you want to make sure the lock is released no matter what.
  3. Calling the reserveSeats method should be done inside separate threads

ReservationSystem.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReservationSystem {
    private Integer totalSeats;
    private final Lock lock = new ReentrantLock();

    public ReservationSystem(Integer totalSeats){
        this.totalSeats = totalSeats;
    }

    public Integer getTotalSeats(){
        return totalSeats;
    }

    public void reserveSeats(String userName, int numOfSeats){
        lock.lock();

        try{
            if(numOfSeats >0 && totalSeats>numOfSeats){
                totalSeats -= numOfSeats;
                System.out.println(userName + " has reserved "+ numOfSeats + " with " + totalSeats + " still available");
            }else{
                System.out.println("Seats not Available");
            }
        }finally {
            lock.unlock();
        }
    }
}

BookSeat.java

public class BookSeat {
    public static void main(String[] args) {
        ReservationSystem objResSys = new ReservationSystem(100);

        System.out.println("Total available Seats "+ objResSys.getTotalSeats());

        Thread objThread1 = new Thread(() -> {objResSys.reserveSeats("User1", 10);});
        Thread objThread2 = new Thread(() -> {objResSys.reserveSeats("User2", 20);});
        Thread objThread3 = new Thread(() -> {objResSys.reserveSeats("User3", 5);});


        objThread1.start();
        objThread2.start();
        objThread3.start();

        try {
            objThread1.join();
            objThread2.join();
            objThread3.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Remaining available Seats "+ objResSys.getTotalSeats());
    }
}


Total available Seats 100
User2 has reserved 20 with 80 still available
User1 has reserved 10 with 70 still available
User3 has reserved 5 with 65 still available
Remaining available Seats 65

Banking System

  1. We have Bank Account with 2 Fields – balance and Account Number
  2. We have Transaction class implementing Runnable
  3. We create object for account with some initial balance and try to pass as parameter to runnable Transaction Object

BankAccount.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BankAccount {
    private Integer balance;
    private Integer accountNumber;

    private final Lock reLock = new ReentrantLock();

    public BankAccount(Integer balance, Integer accountNumber){
        this.balance = balance;
        this.accountNumber = accountNumber;
    }

    public void debitAmount(Integer amount){
        reLock.lock();

        try{
            balance -= amount;
        }finally {
            reLock.unlock();
        }

    }

    public void creditAmount(Integer amount){
        reLock.lock();

        try{
            balance += amount;
        }finally {
            reLock.unlock();
        }
    }

    public Integer getAccountNumber(){
        return this.accountNumber;
    }

    public Integer getBalance(){
        return this.balance;
    }

}

BankTransaction.java

public class BankTransaction implements Runnable{
    public Integer transAmount;
    public BankAccount bankAccount;

    public BankTransaction(Integer transAmount, BankAccount bankAccount){
        this.transAmount  = transAmount;
        this.bankAccount  = bankAccount;
    }


    @Override
    public void run() {
        if(transAmount >= 0){
            bankAccount.creditAmount(transAmount);
        }else{
            bankAccount.debitAmount(Math.abs(transAmount));
        }
    }
}

BankSystem.java

public class BankSystem {
    public static void main(String[] args) {
        BankAccount objAcc1 = new BankAccount(1000, 101);
        BankAccount objAcc2 = new BankAccount(2000, 102);

        Thread objThread1 = new Thread(new BankTransaction(50, objAcc1));
        Thread objThread2 = new Thread(new BankTransaction(-150, objAcc2));
        Thread objThread3 = new Thread(new BankTransaction(250, objAcc2));
        Thread objThread4 = new Thread(new BankTransaction(250, objAcc1));

        objThread1.start();
        objThread2.start();
        objThread3.start();
        objThread4.start();

        try{
            objThread1.join();
            objThread2.join();
            objThread3.join();
            objThread4.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Final Balance in Account " + objAcc1.getAccountNumber() + " with balance " + objAcc1.getBalance());
        System.out.println("Final Balance in Account " + objAcc2.getAccountNumber() + " with balance " + objAcc2.getBalance());
    }
}

Output

Final Balance in Account 101 with balance 1300
Final Balance in Account 102 with balance 2100