This section provides a Java program that simulates the deadlock example - Transferring Funds.
Another illustration of the deadlock problem is the following program,
which simulates multiple threads taking transactions to transfer funds
among different accounts.
/**
* FundTransfer.java
* Copyright (c) 2002 by Dr. Herong Yang
*/
import java.util.*;
public class FundTransfer {
public static final int numberOfAccounts = 50; // number of accounts
public static long[] balance = new long[numberOfAccounts];
public static Random randomGenerator = new Random();
private static final int numberOfThreads = 10;
private static TransferThread[] threadList =
new TransferThread[numberOfThreads];
private static Object[] lockList = new Object[numberOfAccounts];
public static void main(String[] a) {
for (int i=0; i<numberOfAccounts; i++)
lockList[i] = new Object();
for (int i=0; i<numberOfAccounts; i++)
balance[i] = 100000;
ThreadGroup g = new ThreadGroup("The Group");
for (int i=0; i<numberOfThreads; i++) {
TransferThread t = new TransferThread(g,String.valueOf(i));
t.start();
threadList[i] = t;
}
System.out.print("\nNumber of transactions performed per thread:");
long steps = 0;
while (true) {
steps++;
System.out.print("\nStep="+steps+": ");
for (int i=0; i<numberOfThreads; i++)
System.out.print(threadList[i].counts+" ");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println("Interrupted.");
}
}
}
public static boolean doTransfer(int i, int j, long a) {
boolean ok = false;
synchronized (lockList[i]) {
long c = balance[i]; // get balance of the from-account
ok = c-a>=0;
if (ok) { // stop accout balance going negative
try {
Thread.sleep(0,1); // slow down the process
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // re-set the flag
}
synchronized (lockList[j]) {
long d = balance[j]; // get balance of the to-account
ok = d+a>=0;
if (ok) { // stop accout balance going negative
balance[i] = c-a; // update the from-account
balance[j] = d+a; // update the to-account
}
}
}
}
return ok;
}
public static class TransferThread extends Thread {
public long counts;
public TransferThread(ThreadGroup g, String n) {
super(g,n);
counts = 0;
}
public void run() {
while (!isInterrupted()) {
counts++;
Random r = randomGenerator;
int m = numberOfAccounts;
int i = r.nextInt(m); // from-account number
int j = r.nextInt(m); // to-account number
int t = 2*r.nextInt(2)-1; // type of transaction
long a = (long) t*r.nextInt(10000);
if (i!=j) doTransfer(i,j,a);
try {
sleep(r.nextInt(200)); // pause between transactions
} catch (InterruptedException e) {
break;
}
}
}
}
}
The logic of the program is simple. The main thread launches 10 sub threads at the beginning,
and allows each thread to repeatedly transfer some fund from a randomly selected
account to another randomly selected account. The main thread keeps checking
the number of transactions each thread has performed so far.
If it happens that one thread is transferring fund from account A to B, and
another thread is transferring fund from account B to A at the same time,
the two threads will become deadlock to each other. Then, if another thread
is trying to transfer fund from account C to A or B, it will be put on hold
also, because A and B are both locked. Account C will be locked also in this
case. If another thread is trying to transfer fund from A or B to D, it will
also be put on hold, but account D will not be locked. Slowly, all threads
will be put on hold, because of the two deadlocked threads.
Execute the program several times. You will see that it behaves differently
each time. But soon or later, the all threads will be put on hold as predicted.
One of my executions gave the following output: