0

So I have a task to create a program, that has 1 bank account with a starting balance. Then I create a set of cards that can access that account, deposit or withdraw an amount, but not at the same time. I have created a number of threads and then use these threads to access the bank account, withdraw or deposit a random amount, then exit.

My card class:

public class card implements Runnable {
private int id;
private account account;
private int count = 1;


card(int id, account acc1){
    this.id = id;
    this.account = acc1;

}

public int getid(){
    return this.id;
}

public int localBalance(){
    return account.getBalance();
}


public void run() {
    for(count = 0; count <= 5; count++) { //withdraw and deposit random amounts
        int transactionamount = (int)(Math.random()*10);
        if (Math.random() > 0.5) {
        System.out.printf("%-12s%-12s%-12s%-12s\n","card id:(" + getid() + ")"," ",transactionamount, account.getBalance());
        account.deposit(transactionamount);
        } else {
        System.out.printf("%-12s%-12s%-12s%-12s\n","card id:(" + getid() + ")",transactionamount," ",account.getBalance());
        account.withdraw(transactionamount);

        }
        }
        }
}

my account class:

public class account {

private int balance = 5000;


public account(int balance){
    this.balance = balance;
}

public synchronized void withdraw(int amount) {
    balance =   getBalance() - amount;
}

public synchronized void deposit(int amount) {
    balance =   getBalance() + amount;
}

public synchronized int getBalance() {
    return this.balance; 
}


public synchronized void setNewBalance(int localBalance) { //no longer in use
    balance = localBalance;
}

}

my program class

public class Program {

public static void main(String[] args ) throws InterruptedException {
    // TODO Auto-generated method stub

    int numberofcards = 5;
    int accountbalance = 5000;


    card cardArray[] = new card[numberofcards];

    account acc1 = new account(accountbalance); //pass into account the starting balance.

    System.out.printf("\n%-12s%-12s%-12s%-12s\n","Transaction","Withdrawal","Deposit","Balance");
    System.out.printf("%-12s%-12s%-12s%-12s\n"," "," "," ",accountbalance + "\n");

    Thread[] threads = new Thread[numberofcards];

    for(int i = 1; i < numberofcards; i++ ){ 
       cardArray[i] = new card(i, acc1);
       threads[i] = new Thread(cardArray[i]);
       threads[i].start();
    }
    for(int i = 1; i < threads.length; i++ ){
       threads[i].join();
    }
    System.out.printf("\n%-12s%-12s%-12s%-12s\n","finished"," "," ","Final Balance: " + acc1.getBalance()); 


}

}

example of output:

http://puu.sh/lvoE5/05ebcd4c74.png

As you can see, it works however for each different card it goes back to the 5000 and then deposits or withdraws a random amount. I cant seem to figure out why my code doesnt constantly update the amount. Any help would be amazing, im amazed ive got this far by myself anyway.

edit: got rid of the not needed new thread .join code & got rid of synchronized in my run method, output is still the same however, please keep pointing out the problems though, teaching myself about threads is really difficult so ill remember. thank you in advance

edit 2: still not working, directly using withdraw and deposit now. output attached in link

squish
  • 846
  • 12
  • 24
  • You're calling "new Thread()" twice on the same card. One you `start()` and the other you call `join()`. I guess `join()` just returns if a thread isn't started yet. – markspace Nov 23 '15 at 02:11
  • Yes it doesnt seem to have an affect at all, i deleted it and it doesnt change anything. Im not sure how to join threads up that are created from an array – squish Nov 23 '15 at 02:14
  • You throw away the reference to `Thread` created with `new`. Don't do that. Store the reference somewhere, like perhaps in a second array. – markspace Nov 23 '15 at 02:15
  • So are you counting on the fact that your professor has never heard of stackoverflow or that he just don't care about ethics violation or is your university just incredibly lax about those so it doesn't matter? Because when I was TAing asking for help on the internet would've at least caused you to fail the course immediately. – Voo Nov 23 '15 at 09:03

2 Answers2

0

There are multiple problems with your code, first of which is that your testing code will never interweave, as I suspect you wish it to.

Consider the following code:

  for(int i = 1; i < numberofcards; i++ ){ //create amount of cards in array of cards
      cardArray[i] = new card(i, acc1);
      new Thread(cardArray[i]).start();
      new Thread(cardArray[i]).join();
  }

For each loop, you create two thread objects (see new keyword), and for one you start the thread, and the other you call join. Since the second thread is not running, it simply returns and does nothing.

Furthermore, since your run method is synchronized, any new synchronized methods in that class will never run at the same time, which makes no sense whatsoever. See: Should you synchronize the run method? Why or why not?

This is probably what you want:

Thread threads = new Threads[numberofcards];
for(int i = 1; i < numberofcards; i++ ){ 
   cardArray[i] = new card(i, acc1);
   threads[i] = new Thread(cardArray[i]);
   threads[i].start();
}
for(int i = 1; i < threads.length; i++ ){
   threads[i].join();
}

Also, you have a race condition:

You initialize each card with $5000 this.cardBalance = account.getBalance() and you update this.cardBalance. You then update the account at account.setNewBalance(cardBalance) however during the time you set the local variable, and the time you wrote to the account, other threads have also fetched and updated the account's card balance. Your modifications to the account are not atomic.

You should not be using a local variable to represent the balance of the account in the card. This violates the separate entity rule, and has no legitimate purpose.

Community
  • 1
  • 1
Don Scott
  • 3,179
  • 1
  • 26
  • 40
  • 1
    Synchronizing `run()` doesn't do anything. The OP makes new tasks (new card()) so there are multiple object which each have their own lock. I agree it's bloody strange though. – markspace Nov 23 '15 at 02:17
  • @DonScott thank you for your help, this is probably a much better way of making sense of it, however it still has the same output, the bank account balance isnt updated and each thread starts with 5000 when started – squish Nov 23 '15 at 02:35
  • @squish That is correct. You initialize each card with $5000 (`this.cardBalance = account.getBalance()`) and you update only `this.cardBalance`. You never touch the account at all. – Don Scott Nov 23 '15 at 02:54
  • @DonScott i call the method account.setNewBalance(cardBalance); after i have withdrawn or deposited something in my run method – squish Nov 23 '15 at 02:59
  • @squish Yes, but all the threads do that at the same (or similar) time. So if you have five threads in parallel save $5000, add $5, then set $5005, you get $5005, not $5025. – Don Scott Nov 23 '15 at 03:08
  • @DonScott ive tried to cut out all variables within the card class, i now access the account directly by using account.withdraw, account.deposit and account.getBalance to make it work however it still starts at 5000 again everytime a new card starts. im so confused – squish Nov 23 '15 at 03:31
  • @squish If you use `deposit` and `withdraw` as written, it will work. You ***may not*** use `setNewBalance` in your run method without having additional locks. – Don Scott Nov 23 '15 at 03:35
  • @DonScott thanks for the help don, please check my latest edit and see if it makes sense, if not im screwed – squish Nov 23 '15 at 03:42
  • @squish **Yes, that is correct. ** Note that your output code calls getBalance() before the update, and output (`System.out`) may be misordered when accessed by multiple threads. Regardless of the output, the code is synchronized correctly. Kindly accept this as the answer if you have no more questions. :) – Don Scott Nov 23 '15 at 04:13
  • @DonScott I have put the withdraw/deposit before the print statement, however its not correct, im clearly going wrong with the thread synchronization – squish Nov 23 '15 at 04:32
  • @squish You are correctly atomically updating the balance. As I tried to infer, the get/print operation IS NOT atomic with the update. Therefore, I would print only the operations: like "add $5 in thread 3" and so on. Then print the final balance. The final balance should be the starting balance plus all the operations. – Don Scott Nov 23 '15 at 04:44
  • 5000+5+3+9+8-5+2+5+8+1-4+0+6-0-1-1-9+9 = 5036 which is clearly in your output. The answer is correct, go ahead and submit it. :P – Don Scott Nov 23 '15 at 04:47
-1

This should work, notice proper synchronization in Account class

Account:

public class Account {
    private int bankBalance;

    public Account(int initialBankBalance) {
        this.bankBalance = initialBankBalance;
    }

    public synchronized int withdraw(int cardId, int amount) {
        bankBalance = bankBalance - amount;
        System.out.println("Card: " + cardId + "; withdraw: " + amount + "; after withdrawal: " + bankBalance);
        return bankBalance;
    }

    public synchronized int deposit(int cardId, int amount) {
        bankBalance = bankBalance + amount;
        System.out.println("Card: " + cardId + "; deposit: " + amount + "; after deposit: " + bankBalance);
        return bankBalance;
    }

    public synchronized int getBankBalance() {
        return bankBalance;
    }
}

Card:

public class Card implements Runnable {
    private int id;
    private Account account;

    public Card(int id, Account account) {
        this.account = account;
        this.id = id;
    }

    @Override
    public void run() {
        double depositTotal = 0;
        double withdrawTotal = 0;
        for (int i = 0; i < 5; i++) {
            if (Math.random() > 0.5) {
                int deposit = (int) (Math.random() * 10);
                depositTotal = depositTotal + deposit;
                account.deposit(id, deposit);
            } else {
                int withdraw = (int) (Math.random() * 10);
                withdrawTotal = withdrawTotal + withdraw;
                account.withdraw(id, withdraw);
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("Card: " + id + "; withdrawal total: " + withdrawTotal);
        System.out.println("Card: " + id + "; deposit total: " + depositTotal);
    }
}

Program:

    public class Program {

    public static void main(String[] args) throws InterruptedException {
        int numCards = 2;
        int initialBankBalance = 1000;

        Account account = new Account(initialBankBalance);
        System.out.println("Starting balance: " + initialBankBalance);

        Thread[] threads = new Thread[numCards];

        for (int i = 0; i < numCards; i++) {
            Thread thread = new Thread(new Card(i+1, account));
            thread.start();
            threads[i] = thread;
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i].join();
        }

        System.out.println("End balance is : " + account.getBankBalance());

    }
}

Output from a sample run:

Starting balance: 1000
Card: 1; deposit: 3; after deposit: 1003
Card: 2; withdraw: 1; after withdrawal: 1002
Card: 2; deposit: 6; after deposit: 1008
Card: 1; deposit: 3; after deposit: 1011
Card: 1; withdraw: 7; after withdrawal: 1004
Card: 2; deposit: 1; after deposit: 1005
Card: 1; deposit: 0; after deposit: 1005
Card: 2; withdraw: 7; after withdrawal: 998
Card: 1; withdraw: 2; after withdrawal: 996
Card: 2; withdraw: 7; after withdrawal: 989
Card: 2; withdrawal total: 15.0
Card: 1; withdrawal total: 9.0
Card: 2; deposit total: 7.0
Card: 1; deposit total: 6.0
End balance is : 989
chandra
  • 1
  • 2
  • afraid this doesnt work at all, output: http://puu.sh/lvq5L/b1f155d9ec.png thank you for the help though – squish Nov 23 '15 at 04:07
  • @squish: tally all the withdrawals and retrievals, you will see that it matches fine, however, note that the order of statements printed might give you an impression that it is wrong because of interleaving, move the system.out.println() statements inside the Account class, and you will see what i mean – chandra Nov 23 '15 at 04:43
  • @squish: I updated the code to reflect what i meant in my previous comment, find the output in the edited post above – chandra Nov 23 '15 at 04:53