This chapter explores the concept of synchronization and Thread Safety.
Below is a class called Bank. This class has a instance variable balance which is initialized to 100.
The class also has methods to deposit and withdraw.
Lets create a class which will act as a user to this Bank.
This Withdrawer class is a Thread and calls the withdraw method of the bank instance it has.
The test class here creates two instances of withdrawer and passes same bank instace to both of them. When starting both the threads, both will call the withdraw method and try to withdraw money. To avoid balance to go in negative we have added a condition in the Bank.withdraw method.
Now lets try running the code.
As we can see that both threads try to withdraw money. Ideally when a thread has withdrawn the money the next thread should not enter the if (balance >= amount) block. But it does. Because the actual withdrawl of the money i.e this.balance =this.balance- amount; takes some time. In the mean time if another thread tries to call withdraw() it will pass the balance >=amount check and will enter the if block. Since both the threads will withdraw the balance will go in nagitive. To overcome this problem we should restrict second thread to access withdraw method if one thread is already inside it. This can be done by the synchronized keyword. Now lets change the code and run the program again.
The only change we have done here is the synchronized keyword in the withdraw method.
When a Thread accesses the withdraw method it acquired the lock of the object on which this method was called, which in our case is the bank object. An it will release the lock only after it exists the withdraw method. While a thread has a lock of a Object if another thread tries to access a method marked synchronized with the same Object, it will have to wait. That is it will not in the Waiting state and will get access only after the lock on the object is released.
Below is a class called Bank. This class has a instance variable balance which is initialized to 100.
The class also has methods to deposit and withdraw.
public class Bank { int balance = 100; public void deposit(int amount) { this.balance = amount; System.out.println(Thread.currentThread().getName()+" Deposit:" + balance); } public void withdraw(int amount) throws Exception{ if (balance >= amount) { System.out.println(Thread.currentThread().getName()+" Withdrawing:" + balance); Thread.sleep(5000); this.balance =this.balance- amount; System.out.println(Thread.currentThread().getName()+" Withdrawn" + balance); } else { System.out.println(Thread.currentThread().getName()+" Withdraw: entered amount : " + amount + " more than balance:" + balance); } } }
Lets create a class which will act as a user to this Bank.
public class Withdrawer extends Thread { Bank bank; public Withdrawer(Bank bank) { this.bank = bank; } public void run(){ try { bank.withdraw(100); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
This Withdrawer class is a Thread and calls the withdraw method of the bank instance it has.
import java.util.TreeMap; public class TestBank { public static void main(String[] args) { Bank bank = new Bank(); Withdrawer withdrawer = new Withdrawer(bank); Withdrawer withdrawer2 = new Withdrawer(bank); withdrawer.start(); withdrawer2.start(); } }
The test class here creates two instances of withdrawer and passes same bank instace to both of them. When starting both the threads, both will call the withdraw method and try to withdraw money. To avoid balance to go in negative we have added a condition in the Bank.withdraw method.
Now lets try running the code.
Thread-0 Withdrawing:100 Thread-1 Withdrawing:100 Thread-0 Withdrawn0 Thread-1 Withdrawn-100
As we can see that both threads try to withdraw money. Ideally when a thread has withdrawn the money the next thread should not enter the if (balance >= amount) block. But it does. Because the actual withdrawl of the money i.e this.balance =this.balance- amount; takes some time. In the mean time if another thread tries to call withdraw() it will pass the balance >=amount check and will enter the if block. Since both the threads will withdraw the balance will go in nagitive. To overcome this problem we should restrict second thread to access withdraw method if one thread is already inside it. This can be done by the synchronized keyword. Now lets change the code and run the program again.
public class Bank { int balance = 100; public void deposit(int amount) { this.balance = amount; System.out.println(Thread.currentThread().getName()+" Deposit:" + balance); } public synchronized void withdraw(int amount) throws Exception{ if (balance >= amount) { System.out.println(Thread.currentThread().getName()+" Withdrawing:" + balance); Thread.sleep(5000); this.balance =this.balance- amount; System.out.println(Thread.currentThread().getName()+" Withdrawn" + balance); } else { System.out.println(Thread.currentThread().getName()+" Withdraw: entered amount : " + amount + " more than balance:" + balance); } } }
The only change we have done here is the synchronized keyword in the withdraw method.
Thread-0 Withdrawing:100 Thread-0 Withdrawn0 Thread-1 Withdraw: entered amount : 100 more than balance:0
When a Thread accesses the withdraw method it acquired the lock of the object on which this method was called, which in our case is the bank object. An it will release the lock only after it exists the withdraw method. While a thread has a lock of a Object if another thread tries to access a method marked synchronized with the same Object, it will have to wait. That is it will not in the Waiting state and will get access only after the lock on the object is released.
No comments:
Post a Comment