I have a method transfer() which withdrawals money from one account and deposits it into another. There are 10 accounts each running with their own thread. I have another method test() which sums up the total in each account to make sure that the bank has not lost or gained money. In order to have an accurate total, I created a boolean flag to indicate if testing is in progress. If it is, I need to somehow suspend the transfers until the test is finished. I've tried to implement this using a synchronized block to tell the threads to wait on a condition and release once the condition is no longer true. For some reason I'm having difficulty. My transfer method looks like this:
public class Bank {
public static final int NTEST = 10;
private Account[] accounts;
private long ntransacts = 0;
private int initialBalance;
private int numAccounts;
private boolean open;
private int transactsInProgress;
private boolean testing=false;
public Bank(int numAccounts, int initialBalance) {
open = true;
this.initialBalance = initialBalance;
this.numAccounts = numAccounts;
accounts = new Account[numAccounts];
for (int i = 0; i < accounts.length; i++) {
accounts[i] = new Account(this, i, initialBalance);
}
ntransacts = 0;
transactsInProgress = 0;
}
public synchronized void incrementTransacts(){
transactsInProgress++;
}
public synchronized void decrementTransacts(){
transactsInProgress--;
}
public void transfer(int from, int to, int amount) throws InterruptedException {
accounts[from].waitForAvailableFunds(amount);
synchronized(this){
while(testing){
System.out.println("Cannot transfer while testing...");
this.wait();
}
}
if (!open) return;
if (accounts[from].withdraw(amount)) {
incrementTransacts(); //synchronzied method increments transactsInProgress
accounts[to].deposit(amount);
decrementTransacts(); //synchronized method
}
if (shouldTest()) test();
synchronized(this){
this.notifyAll();
}
}
public synchronized void test() throws InterruptedException {
int sum = 0;
testing=true;
while(transactsInProgress!=0){
System.out.println("Cannot test while transactions are in progres... \nWaiting...");
wait();
}
for (int i = 0; i < accounts.length; i++) {
System.out.printf("%s %s%n",
Thread.currentThread().toString(),accounts[i].toString());
sum += accounts[i].getBalance();
}
System.out.println(Thread.currentThread().toString() +
" Sum: " + sum);
if (sum != numAccounts * initialBalance) {
System.out.println(Thread.currentThread().toString() +
" Money was gained or lost");
System.exit(1);
} else {
System.out.println(Thread.currentThread().toString() +
" The bank is in balance");
}
testing=false;
notifyAll();
}
public int size() {
return accounts.length;
}
public synchronized boolean isOpen() {return open;}
public void closeBank() {
synchronized (this) {
open = false;
}
for (Account account : accounts) {
synchronized(account) {
account.notifyAll();
}
}
}
public synchronized boolean shouldTest() {
return ++ntransacts % NTEST == 0;
}
}
It's been a while since I've coded in Java and I'm new to threads and concurrency so I'm not sure exactly where I'm going wrong. When I run the program, the bank sum is incorrect. Theres 10,000 in each account so the sum each time should be 100,000. Any ideas here?
EDIT: The thread class and Main:
class TransferThread extends Thread {
public TransferThread(Bank b, int from, int max) {
bank = b;
fromAccount = from;
maxAmount = max;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
int toAccount = (int) (bank.size() * Math.random());
int amount = (int) (maxAmount * Math.random());
bank.transfer(fromAccount, toAccount, amount);
}
bank.closeBank();
}
private Bank bank;
private int fromAccount;
private int maxAmount;
}
Main:
public static void main(String[] args) throws InterruptedException {
Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
Thread[] threads = new Thread[NACCOUNTS];
// Start a thread for each account
for (int i = 0; i < NACCOUNTS; i++) {
threads[i] = new TransferThread(b, i, INITIAL_BALANCE);
threads[i].start();
}
// Wait for all threads to finish
for (int i = 0; i < NACCOUNTS; i++) {
try {
threads[i].join();
} catch (InterruptedException ex) {
// Ignore this
}
}
b.test();
}