Java Tutorials - Herong's Tutorial Examples - Version 7.00, by Dr. Herong Yang

BankingThread.java - Synchronization Sample Program

This section provides a tutorial example on how to solve the banking synchronization issue with Java synchronized methods.

Now, let's write a Java program to see how the synchronization technique can solve the bank problem. Two classes are designed to simulate a bank system:

  • BankingThread class: Acting as an ATM machine, taking deposit or withdraw transaction requests from customers, and asking the BankingMain class to perform the transactions.
  • BankingMain class: Acting as the control center, managing instances of the BankingThread class, and performing transactions coming from each BankingThread instance.

BankingThread class:

/* BankingThread.java
 - Copyright (c) 2014, HerongYang.com, All Rights Reserved.
 */
import java.util.*;
public class BankingThread extends Thread {
   private long[] t_balance; // acount balance
   private long t_count; // number of transactions done so far
   public BankingThread(ThreadGroup g, String n) {
      super(g,n);
      t_count = 0;
      int m = BankingMain.accountCount();
      t_balance = new long[m];
      for (int i=0; i<m; i++) t_balance[i] = 0;
   }
   public long transactionCount() {
      return t_count;
   }
   public long getBalance(int i) {
      return t_balance[i];
   }
   public void run() {
      while (!isInterrupted()) {
         t_count++; 
         Random r = BankingMain.getRandom();
         long t_step = BankingMain.getStep();
         int m = BankingMain.accountCount();
         int n = r.nextInt(m); // account number
         int t = 2*r.nextInt(2)-1; // type of transaction
         long a = (long) t*r.nextInt(10000); // amount of transaction
         if (t_step==1) 
            if (BankingMain.doTransaction1(n,a)) t_balance[n] += a;
         if (t_step==2)
            if (BankingMain.doTransaction2(n,a)) t_balance[n] += a;
         if (t_step==3)
            if (BankingMain.doTransaction3(n,a)) t_balance[n] += a;
         if (t_step==4)
            if (BankingMain.doTransaction4(n,a)) t_balance[n] += a;
         try {
            sleep(10); // wait for the next customer
         } catch (InterruptedException e) {
            break;
         }
      }
   }
}

BankingMain class:

/* BankingMain.java
 - Copyright (c) 2014, HerongYang.com, All Rights Reserved.
 */
import java.util.*;
public class BankingMain {
   public static final int a_maxi = 10; // number of accounts
   public static long[] balance = new long[a_maxi];
   public static Random r_number;
   private static final int t_maxi = 5; // number of threads
   private static BankingThread[] t_list = new BankingThread[t_maxi];
   private static long t_step;
   private static Object o_lock;
   private static Object[] a_lock = new Object[a_maxi];
   public static void main(String[] a) {
      o_lock = new Object();
      for (int i=0; i<a_maxi; i++) a_lock[i] = new Object();
      t_step = 1; // using doTransaction1();
      System.out.println("No synchronization:");
      testATM();
      printResult();
      t_step = 2; // using doTransaction2();
      System.out.println("Synchronized method:");
      testATM();
      printResult();
      t_step = 3; // using doTransaction3();
      System.out.println("Synchronized statements:");
      testATM();
      printResult();
      t_step = 4; // using doTransaction4();
      System.out.println("Synchronized statements per account:");
      testATM();
      printResult();
   }
   private static void testATM() {
      for (int i=0; i<a_maxi; i++) balance[i] = 0;
      r_number = new Random();
      ThreadGroup g = new ThreadGroup("ATM");
      for (int i=0; i<t_maxi; i++) {
         BankingThread t = new BankingThread(g,String.valueOf(i));
         t.start();
         t_list[i] = t;
      }
      try {
         Thread.sleep(5*1000); // working period
      } catch (InterruptedException e) {
         System.out.println("Interrupted in the mainthread.");
      }
      g.interrupt();
      while (g.activeCount()>0); // wait for all threads to end
   }
   private static void printResult() {
      System.out.print("Account");
      for (int i=0; i<t_maxi; i++)
         System.out.print(", ATM "+i);
      System.out.print(", Transaction Sum, Balance");
      for (int j=0; j<a_maxi; j++) {
         System.out.print("\n"+j);
         long sum = 0;
         for (int i=0; i<t_maxi; i++) {
            sum += t_list[i].getBalance(j);
            System.out.print(", "
               +getCurrency(t_list[i].getBalance(j)));
         }
         System.out.print(", "+getCurrency(sum)+
            ", "+getCurrency(balance[j]));
      }
      System.out.print("\n# of Transactions");
      for (int i=0; i<t_maxi; i++)
         System.out.print(", "+t_list[i].transactionCount());
      System.out.print("\n");
   }
   private static String getCurrency(long d) {
      long i = d/100;
      long f = Math.abs(d)%100;
      String fs = String.valueOf(f);
      if (f<10) fs = "0"+fs;
      return String.valueOf(i)+"."+fs;
   }
   public static long getStep() {
      return t_step;
   }
   public static int accountCount() {
      return a_maxi;
   }
   public static Random getRandom() {
      return r_number;
   }
   public static long getBalance(int i) {
      return balance[i];
   }
   public static boolean doTransaction1(int i, long a) {
      boolean ok = false;
      long c = balance[i]; // get current balance
      ok = c+a>0;
      if (ok) { // stop accout balance going negative
         try {
            Thread.sleep(1); // slow down the process
         } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // re-set the flag
         }
         balance[i] = c+a; // set current balance
      }
      return ok;
   }
   public synchronized static boolean doTransaction2(int i, long a) {
      boolean ok = false;
      long c = balance[i]; // get current balance
      ok = c+a>0;
      if (ok) { // stop accout balance going negative
         try {
            Thread.sleep(1); // slow down the process
         } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // re-set the flag
         }
         balance[i] = c+a; // set current balance
      }
      return ok;
   }
   public static boolean doTransaction3(int i, long a) {
      boolean ok = false;
      synchronized (o_lock) {
         long c = balance[i]; // get current balance
         ok = c+a>0;
         if (ok) { // stop accout balance going negative
            try {
               Thread.sleep(1); // slow down the process
            } catch (InterruptedException e) {
               Thread.currentThread().interrupt(); // re-set the flag
            }
            balance[i] = c+a; // set current balance
         }
      }
      return ok;
   }
   public static boolean doTransaction4(int i, long a) {
      boolean ok = false;
      synchronized (a_lock[i]) {
         long c = balance[i]; // get current balance
         ok = c+a>0;
         if (ok) { // stop accout balance going negative
            try {
               Thread.sleep(1); // slow down the process
            } catch (InterruptedException e) {
               Thread.currentThread().interrupt(); // re-set the flag
            }
            balance[i] = c+a; // set current balance
         }
      }
      return ok;
   }
}

Couple of notes about this program:

  • BankingThread also keeps track of balance of each account with t_balance[]. The sum of t_balance[i] from every thread should equal to the balance in the BankingMain class, balance[i].
  • BankingTread.run() simulates a transaction from a customer by randomly generating an account number, a transaction type, and a transaction amount.
  • I use "long" to store and calculate currency related values, because it is more accurate than "double".
  • The test steps are used to control the tests of different synchronization implementations: no synchronization, synchronized method, synchronized statements, and synchronized statements per account.
  • The Thread.sleep(1) statement in doTransaction() methods is used to increase the execution time of the transaction. If the thread is signaled for interruption during this sleep, no action is taken, but re-set the interruption flag to let BankingThread class to handle it later.
  • All instances of BankingThread are created within a ThreadGroup, so I can easily send an interruption signal to all of them.
  • The "while (g.activeCount()>0);" statement in the BankingMain class is used to make sure all treads are terminated before collecting results from them.

See the next section for the execution output.

Last update: 2014.

Table of Contents

 About This Book

 Installing JDK 1.8 on Windows

 Execution Process, Entry Point, Input and Output

 Primitive Data Types and Literals

 Bits, Bytes, Bitwise and Shift Operations

 Managing Bit Strings in Byte Arrays

 Reference Data Types and Variables

 StringBuffer - The String Buffer Class

 System Properties and Runtime Object Methods

 Generic Classes and Parameterized Types

 Generic Methods and Type Inference

 Lambda Expressions and Method References

 Execution Threads and Multi-Threading Java Programs

 ThreadGroup Class and "system" ThreadGroup Tree

Synchronization Technique and Synchronized Code Blocks

 Why Synchronization Is Needed in Multi-Threading Applications?

 Synchronization Technique - Lock and Synchronized Code

 "synchronized" - How Java Supports Synchronization

BankingThread.java - Synchronization Sample Program

 BankingThread.java - Synchronization Sample Program Output

 Deadlock Condition Example Programs

 Garbage Collection and the gc() Method

 Outdated Tutorials

 References

 PDF Printing Version

BankingThread.java - Synchronization Sample Program - Updated in 2014, by Dr. Herong Yang