In order to understand one must have a strong grasp over Java OOPs, Java Multithreading & Java Interrupted-Exception. If not go through them as the title in itself is a sheer implementation of multithreading.
Approaches:
- Rookie approach
- Multithreading approach
- Synchronization invoking in multithreading approach
In order to understand, let us consider an illustration in order to implement the approach.
Illustration:
We will discuss the architecture of the banking transaction system using java. Throughout this editorial, I will hold your hands and take you through the entire transaction procedure and make it Easy-Pease for you to understand so that you can even explain it to your friends. For the sake of simplicity, we have considered a joint bank account having 5 owners(Arnab, Monodwip, Mukta, Rinkel, and Shubham) and the initial balance is a hundred dollars ($100). The transactions of the account are listed as follows:
Name | Balance($) | Withdrawal($) | Deposit($) | Comment | Final Balance($) |
---|---|---|---|---|---|
Arnab | 100 | 20 |
Arnab has withdrawn 20 Balance after withdrawal: 80 |
80 | |
Monodwip | 80 | 40 |
Monodwip withdrawn 40 Balance after withdrawal: 40 |
40 | |
Mukta | 40 | 35 |
Mukta deposited 35 Balance after deposit: 75 |
75 | |
Rinkel | 75 | 80 |
Rinkel you can not withdraw 80 your balance is: 75 |
75 | |
Shubham | 75 | 40 |
Shubham withdrawn 40 Balance after withdrawal: 35 |
35 |
Approach 1: Rookie approach
We have declared the “withdraw” and “deposit” method inside the class “Bank” and accessed them from the driver class “GFG” by creating an object “obj” of Bank class.
Example
Java
// Java Program to illustrate Rookie Approach // In Banking transaction system // Class 1 // Bank class // Defining the banking transaction class Bank { // Initial balance $100 int total = 100 ; // Money withdrawal method. Withdraw only if // total money greater than or equal to the money // requested for withdrawal // Method // To withdraw money void withdrawn(String name, int withdrawal) { if (total >= withdrawal) { System.out.println(name + " withdrawn " + withdrawal); total = total - withdrawal; System.out.println( "Balance after withdrawal: " + total); // Making the thread sleep for 1 second after // each withdrawal // Try block to check for exceptions try { // Making thread t osleep for 1 second Thread.sleep( 1000 ); } // Catch block to handle the exceptions catch (InterruptedException e) { // Display the exception along with line // number // using printStacktrace() method e.printStackTrace(); } } // If the money requested for withdrawal is greater // than the balance then deny transaction*/ else { // Print statements System.out.println(name + " you can not withdraw " + withdrawal); System.out.println( "your balance is: " + total); // Making the thread sleep for 1 second after // each transaction failure // Try block to check for exceptions try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } // Method - to deposit money // Accept money whenever deposited void deposit(String name, int deposit) { System.out.println(name + " deposited " + deposit); total = total + deposit; System.out.println( "Balance after deposit: " + total); // Making the thread sleep for 1 second after // each deposit try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } // Class 2 // main class class GFG { // Main driver method public static void main(String[] args) { // Declaring an object of Bank class and calling the // withdarwn and deposit methods with suitable // parameters // Creating object of class 1 inside main() Bank obj = new Bank(); // Custom input - Transactions obj.withdrawn( "Arnab" , 20 ); obj.withdrawn( "Monodwip" , 40 ); obj.deposit( "Mukta" , 35 ); obj.withdrawn( "Rinkel" , 80 ); obj.withdrawn( "Shubham" , 40 ); } } |
Output:
C:\Users\USER\Desktop\LearnCoding\MultiThreading>javac GFG.java C:\Users\USER\Desktop\LearnCoding\MultiThreading>java GFG Arnab withdrawn 20 Balance after withdrawal: 80 //After 1 Second Monodwip withdrawn 40 Balance after withdrawal: 40 //After 1 Second Mukta deposited 35 Balance after deposit: 75 //After 1 Second Rinkel you can not withdraw 80 your balance is: 75 //After 1 Second Shubham withdrawn 40 Balance after withdrawal: 35
There are certain cons associated with the Rookie approach as depicted below:
No 2 people can make transactions at the same time, one needs to wait till the former finishes its transaction. If the number of people is large then we need to wait and wait until our turn comes. To demonstrate this problem, we made the thread sleep for 3 seconds during each transaction in the video provided below. In real life, it would take much time making this approach incapable of implementation in real transaction projects.
Method 2: Multithreading Approach
How multithreading can help?
Multithreading allows different threads to work at the same time without having any dependency on one-another. So large group of threads can perform an operation at the same time.
Example
Java
// Java Program to illustrate Multithreading Approach // In Banking transaction system // Class 1 // Helper class class Bank { // Initial custom balance int total = 100 ; // Money withdrawal method. Withdraw only if total money // greater than or equal to the money requested for // withdrawal void withdrawn(String name, int withdrawal) { if (total >= withdrawal) { System.out.println(name + " withdrawn " + withdrawal); total = total - withdrawal; System.out.println(total); // Making the thread sleep for 1 second after // each withdrawal // Try block to check for exceptions try { // Making thread to sleep for 1 second Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } // Else if the money requested for withdrawal is // greater than the balance then deny transaction else { System.out.println(name + " you can not withdraw " + withdrawal); System.out.println( "your balance is: " + total); // Making the thread sleep for 1 second after // each transaction failure try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } // Method - To deposit money // Accepting money whenever deposited void deposit(String name, int deposit) { System.out.println(name + " deposited " + deposit); total = total + deposit; System.out.println( "Balance after deposit: " + total); // Making the thread sleep for 1 second after // each deposit try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } // Method - Withdraw method // Called from ThreadWithdrawal class // using the object of Bank class passed // from the main() method class ThreadWithdrawal extends Thread { Bank object; String name; int dollar; // Constructor of this method ThreadWithdrawal(Bank ob, String name, int money) { this .object = ob; this .name = name; this .dollar = money; } // run() method for thread public void run() { object.withdrawn(name, dollar); } } // Deposit method is called from ThreadDeposit class // using the object of Bank class passed // from the main method class ThreadDeposit extends Thread { Bank object; String name; int dollar; ThreadDeposit(Bank ob, String name, int money) { // This keyword refers t ocurrent instance itself this .object = ob; this .name = name; this .dollar = money; } public void run() { object.deposit(name, dollar); } } // Class 2 // Main class class GFG { // Main driver method public static void main(String[] args) { // Declaring an object of Bank class and passing the // object along with other parameters to the // ThreadWithdrawal and ThreadDeposit class. This // will be required to call withdrawn and deposit // methods from those class // Creating an object of class1 Bank obj = new Bank(); ThreadWithdrawal t1 = new ThreadWithdrawal(obj, "Arnab" , 20 ); ThreadWithdrawal t2 = new ThreadWithdrawal(obj, "Monodwip" , 40 ); ThreadDeposit t3 = new ThreadDeposit(obj, "Mukta" , 35 ); ThreadWithdrawal t4 = new ThreadWithdrawal(obj, "Rinkel" , 80 ); ThreadWithdrawal t5 = new ThreadWithdrawal(obj, "Shubham" , 40 ); // When a program calls the start() method, a new // thread is created and then the run() method is // executed. // Starting threads created above t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } } |
Output:
Now there are certain problems with the multithreading approach as listed below:
When multiple threads try to do a particular operation at the same time then there exists a possibility of bad output, this is because all the threads are updating the same resource at a time. In the above output, we got balance in negative figures due to the same reason.
How to handle those cases where multiple people try to access the same operation at a time?
If multiple threads access a single resource at a time then there exists a possibility of bad output. This unwanted phenomenon is defined as data racing.
Suppose we have $100 in our joint bank account. To trick the banker, both of us can request $100 simultaneously at a time. The system will create an object, assign 2 threads and pass them to the withdrawal method. At the end of the process, both of us will have $100!
To handle this problem engineers came up with the synchronization concept.
Method 3: Incorporating synchronization with multithreading
Synchronization provides a lock to the object and declares a sensitive area (withdraw & deposit methods). An object can have multiple threads but the sensitive area can only be accessed by 1 thread at a time. The thread scheduler chooses the order of execution of the threads. As it is a random process the output is different for each interpretation.
Why should we use static synchronization?
Say we have 5 thread classes with 1 object each. Each object have multiple threads. Now the sensitive area will be accessed by 5 threads at a time! To handle this problem engineers came up with the idea of static synchronization. We provide a lock to the class. The class will select 1 object at a time. The object in turn will choose 1 thread and pass it through the sensitive area.
Does synchronized multithreaded execution slower than the normal execution without multithreading?
No. The amount of time one task spends waiting for another is considered as overhead. In synchronized multithreading, this overhead time can be used to do other productive work until the waiting thread gets the key from the thread scheduler to get inside the synchronized area. Thus the overhead would be minimal in the case of synchronized multithreaded execution, so we can expect it to be faster.
Example
Java
// Java Program to illustrate Multithreading Approach // With Synchronization In Banking transaction system // Class 1 // Helper class class Bank { // Initial balance $100 static int total = 100 ; // Money withdrawal method. Withdraw only if total money // greater than or equal to the money requested for // withdrawal static synchronized void withdrawn(String name, int withdrawal) { if (total >= withdrawal) { System.out.println(name + " withdrawn " + withdrawal); total = total - withdrawal; System.out.println( "Balance after withdrawal: " + total); /* Making the thread sleep for 1 second after each withdrawal.*/ try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } // If the money requested for withdrawal is greater // than the balance then deny transaction else { System.out.println(name + " you can not withdraw " + withdrawal); System.out.println( "your balance is: " + total); // Making the thread sleep for 1 second after // each transaction failure // Try block to check for exceptions try { // Making thread to sleep for 1 second Thread.sleep( 1000 ); } // Catch bloc kto handle exceptions catch (InterruptedException e) { // Display the line number where exception // occurred // Using printStackTrace() method e.printStackTrace(); } } } // Method - Deposit method // Accepting money whenever deposited static synchronized void deposit(String name, int deposit) { System.out.println(name + " deposited " + deposit); total = total + deposit; System.out.println( "Balance after deposit: " + total); // Making the thread sleep for 1 second // after each deposit // Try block to check for exceptions try { // Making thread to sleep for 1 second Thread.sleep( 1000 ); } // Catch block to handle InterruptedException // exception catch (InterruptedException e) { e.printStackTrace(); } } } // Method - Withdraw // It is called from ThreadWithdrawal class using // the object of Bank class passed from the main method class ThreadWithdrawal extends Thread { // Attributes of this class Bank object; String name; int dollar; // Constructor of this class ThreadWithdrawal(Bank ob, String name, int money) { // This keyword refers to parent class this .object = ob; this .name = name; this .dollar = money; } // run() method for the thread public void run() { object.withdrawn(name, dollar); } } // Deposit method is called from ThreadDeposit class using // the object of Bank class passed from the main method*/ // Class 2 // Helper class extending Thread class class ThreadDeposit extends Thread { Bank object; String name; int dollar; ThreadDeposit(Bank ob, String name, int money) { this .object = ob; this .name = name; this .dollar = money; } public void run() { object.deposit(name, dollar); } } // Class 3 // Main class class GFG { // Main driver method public static void main(String[] args) { // Declaring an object of Bank class and passing the // object along with other parameters to the // ThreadWithdrawal and ThreadDeposit class. This // will be required to call withdrawn and deposit // methods from those class // Creating object of above class inside main() Bank obj = new Bank(); // Creating threads ThreadWithdrawal t1 = new ThreadWithdrawal(obj, "Arnab" , 20 ); ThreadWithdrawal t2 = new ThreadWithdrawal(obj, "Monodwip" , 40 ); ThreadDeposit t3 = new ThreadDeposit(obj, "Mukta" , 35 ); ThreadWithdrawal t4 = new ThreadWithdrawal(obj, "Rinkel" , 80 ); ThreadWithdrawal t5 = new ThreadWithdrawal(obj, "Shubham" , 40 ); // When a program calls the start() method, a new // thread is created and then the run() method is // executed t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } } |
Output(Compiled & Interpreted):
Output(Interpreted): Shubham & Monodwip failed to withdraw money
C:\Users\USER\Desktop\Network Java>java GFG Arnab withdrawn 20 Balance after withdrawal: 80 Rinkel withdrawn 80 Balance after withdrawal: 0 Shubham you can not withdraw 40 your balance is: 0 Mukta deposited 35 Balance after deposit: 35 Monodwip you can not withdraw 40 your balance is: 35
Output(Interpreted): Rinkel failed to withdraw money.
C:\Users\USER\Desktop\Network Java>java GFG Arnab withdrawn 20 Balance after withdrawal: 80 Shubham withdrawn 40 Balance after withdrawal: 40 Monodwip withdrawn 40 Balance after withdrawal: 0 Mukta deposited 35 Balance after deposit: 35 Rinkel you can not withdraw 80 your balance is: 35
Output(Interpreted): Monodwip failed to withdraw money.
C:\Users\USER\Desktop\Network Java>java GFG Arnab withdrawn 20 Balance after withdrawal: 80 Rinkel withdrawn 80 Balance after withdrawal: 0 Shubham you can not withdraw 40 your balance is: 0 Monodwip you can not withdraw 40 your balance is: 0 Mukta deposited 35 Balance after deposit: 35
Note: The thread scheduler chooses the order of execution of the threads. As it is a random process the output is different for each interpretation.