diff --git a/src/edu/temple/cis/c3238/banksim/Account.java b/src/edu/temple/cis/c3238/banksim/Account.java index 4082223..2b87dd5 100644 --- a/src/edu/temple/cis/c3238/banksim/Account.java +++ b/src/edu/temple/cis/c3238/banksim/Account.java @@ -1,5 +1,8 @@ package edu.temple.cis.c3238.banksim; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * @author Cay Horstmann * @author Modified by Paul Wolfgang @@ -10,21 +13,29 @@ public class Account { private volatile int balance; private final int id; private final Bank myBank; - + public Account(Bank myBank, int id, int initialBalance) { this.myBank = myBank; this.id = id; balance = initialBalance; } + /* + * getBalance() does not need to be synchronized, despite balance being + * a shared variable, because getBalance() does not modify balance. + **/ public int getBalance() { return balance; } - public boolean withdraw(int amount) { + /* + * withdraw() method is synchronized via synchronized keyword in it's + * declaration + **/ + public synchronized boolean withdraw(int amount) { if (amount <= balance) { int currentBalance = balance; -// Thread.yield(); // Try to force collision + //Thread.yield(); // Try to force collision int newBalance = currentBalance - amount; balance = newBalance; return true; @@ -33,15 +44,33 @@ public boolean withdraw(int amount) { } } - public void deposit(int amount) { + /* + * synchronized same as withdraw() + **/ + public synchronized void deposit(int amount) { int currentBalance = balance; -// Thread.yield(); // Try to force collision + //Thread.yield(); // Try to force collision int newBalance = currentBalance + amount; balance = newBalance; + + notifyAll(); + } + + /* + * implementation of task 4 + * this method waits for amount <= balance + **/ + public synchronized void waitForSufficientFunds(int amount) { + + while (myBank.isOpen() && amount >= balance) { + try { + wait(); + } catch (InterruptedException ex) {/*ignore*/} + } } @Override public String toString() { return String.format("Account[%d] balance %d", id, balance); } -} +} \ No newline at end of file diff --git a/src/edu/temple/cis/c3238/banksim/Bank.java b/src/edu/temple/cis/c3238/banksim/Bank.java index e419ba8..af4d22c 100644 --- a/src/edu/temple/cis/c3238/banksim/Bank.java +++ b/src/edu/temple/cis/c3238/banksim/Bank.java @@ -1,5 +1,6 @@ package edu.temple.cis.c3238.banksim; +import java.util.concurrent.locks.ReentrantLock; /** * @author Cay Horstmann * @author Modified by Paul Wolfgang @@ -13,7 +14,10 @@ public class Bank { private long ntransacts = 0; private final int initialBalance; private final int numAccounts; + private final ReentrantLock rlock = new ReentrantLock(); + private boolean open = true; + public Bank(int numAccounts, int initialBalance) { this.initialBalance = initialBalance; this.numAccounts = numAccounts; @@ -25,20 +29,55 @@ public Bank(int numAccounts, int initialBalance) { } public void transfer(int from, int to, int amount) { -// accounts[from].waitForAvailableFunds(amount); - if (accounts[from].withdraw(amount)) { - accounts[to].deposit(amount); + + if (!open) { + return; + } + + /* waiting for amount <= balance via method call */ + accounts[from].waitForSufficientFunds(amount); + + /* ReentrantLock used to manipulate account balances */ + rlock.lock(); + try { + if (accounts[from].withdraw(amount)) { + accounts[to].deposit(amount); + } + } + finally { + rlock.unlock(); + } + + if (shouldTest()) { + test(); } - if (shouldTest()) test(); - } + +} public void test() { + int sum = 0; - for (Account account : accounts) { - System.out.printf("%s %s%n", + + /* + * the shared part of this method is "account.getBalance" + * hence, we lock that part with a ReentrantLock + **/ + rlock.lock(); + + try { + + for (Account account : accounts) { + System.out.printf("%s %s%n", Thread.currentThread().toString(), account.toString()); - sum += account.getBalance(); + sum += account.getBalance(); + } + + } + + finally { + rlock.unlock(); } + System.out.println(Thread.currentThread().toString() + " Sum: " + sum); if (sum != numAccounts * initialBalance) { @@ -51,6 +90,7 @@ public void test() { } } + public int size() { return accounts.length; } @@ -59,5 +99,27 @@ public int size() { public boolean shouldTest() { return ++ntransacts % NTEST == 0; } + + /* returns true when the account is open, false otherwise */ + public synchronized boolean isOpen() { + return open; + } + + /* method to satisfy Task 5; + * closeBank() closes all accounts */ + public void closeBank() { + + /* the open variable is shared and hence protected */ + synchronized (this) { + open = false; + } + + /* wake up all waiting threads */ + for (Account account : accounts) { + synchronized (account) { + account.notifyAll(); + } + } + } -} +} \ No newline at end of file diff --git a/src/edu/temple/cis/c3238/banksim/BankSimMain.java b/src/edu/temple/cis/c3238/banksim/BankSimMain.java index 7357332..04d8f89 100644 --- a/src/edu/temple/cis/c3238/banksim/BankSimMain.java +++ b/src/edu/temple/cis/c3238/banksim/BankSimMain.java @@ -18,10 +18,6 @@ public static void main(String[] args) { threads[i] = new TransferThread(b, i, INITIAL_BALANCE); threads[i].start(); } - -// b.test(); System.out.printf("Bank transfer is in the process.\n"); } -} - - +} \ No newline at end of file diff --git a/src/edu/temple/cis/c3238/banksim/TransferThread.java b/src/edu/temple/cis/c3238/banksim/TransferThread.java index 7c9b8eb..37b0f38 100644 --- a/src/edu/temple/cis/c3238/banksim/TransferThread.java +++ b/src/edu/temple/cis/c3238/banksim/TransferThread.java @@ -23,5 +23,8 @@ public void run() { int amount = (int) (maxAmount * Math.random()); bank.transfer(fromAccount, toAccount, amount); } + + /* when one bank closes, it calls closeBank (closing them all) */ + bank.closeBank(); } }