用Java实现银行账户

时间:2011-05-18 13:11:40

标签: java synchronization

我是Java中的线程编程新手。要理解线程,我正在尝试编写一个简单的程序来模拟银行账户。我刚刚实施了撤销并尝试对其进行测试。 输出的前几行是在下面。

T2退出前的余额:1000
T2退出后的余额:990
T1退出前的余额:1000
T1退出后的余额:980
T2退出前的余额:980
T2退出后的余额:970
T1退出前的余额:970
T1退出后的余额:960左

我的问题是为什么输出中的第3行(T1提取前的余额:1000)给出1000而不是990.如果它是正确的,它应该在第2行。我错过了一些东西。我的方法是否正确?

我的猜测是两个线程试图写入控制台并且线程T1只是没有机会在第二行写入。

class BankAccount {

    private volatile int balance;

    public BankAccount(int b){
        balance = b;
    }

    public BankAccount(){
        balance = 0;
    }


    synchronized public int getBalance(){
        return balance;
    }

    synchronized public int withdraw(int w)
    {
        int b = getBalance();
        if(w <= b){
            balance = balance-w;
            return w;
        }
        else
            return 0;
    }
}

class WithdrawAccount implements Runnable{

    private BankAccount acc;
    private int amount;

    public WithdrawAccount(){
        acc = null;
        amount = 0;
    }

    public WithdrawAccount(BankAccount acc,int amount){
        this.acc = acc;
        this.amount = amount;
    }

    public void run() {
        int w; 

        for(int i =0; i<20; i++){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            System.out.println("Balance before "+Thread.currentThread().getName()+" withdrawl: "+acc.getBalance());
            w = acc.withdraw(amount);
            System.out.println("Balance after "+Thread.currentThread().getName()+" withdrawl: "+acc.getBalance());
            //System.out.println("amount with drawn by: "+Thread.currentThread().getName()+" "+w);

        }

    }

}

public class TestBankAccount{

    public static void main(String[] args) {

        BankAccount b = new BankAccount(1000);
        WithdrawAccount w = new WithdrawAccount(b,10);
        Thread wt1 = new Thread(w);
        wt1.setName("T1");

        Thread wt2 = new Thread(w);
        wt2.setName("T2");

        wt1.start();
        wt2.start();
    }
}

4 个答案:

答案 0 :(得分:2)

可能有一个线程在这些行之间调用withdraw():

System.out.println("Balance before "+Thread.currentThread().getName()+" withdrawl: "+acc.getBalance());
w = acc.withdraw(amount);

可能的情况:

  1. T2调用getBalance();
  2. T1调用getBalance();
  3. T2出局平衡;
  4. T2呼叫撤回();
  5. T2退出后取消平衡;
  6. T1出局平衡;
  7. T1调用withdraw();
  8. 退出后T1出局平衡;

答案 1 :(得分:2)

你没有做任何事情来同步你的run方法,所以在撤回之前和之后的printlns和撤回本身都不是原子的。你正在进行线程交错。

答案 2 :(得分:1)

这是可能的线程交错之一:

    wt2: calls getBalance()   // retrieves 1000
    wt2: prints "Balance before T2 withdrawl: 1000"

    wt1: calls getBalance()   // retrieves 1000 also

    wt2: acc.withdraw(amount) // balance is now 990
    wt2: prints "Balance after T2 withdrawl: 990"

    wt1: acc.withdraw(amount)    // balance was 990 after wt1 withdraws. wt1 now withdraws again so balance is 980 
    wt1: prints "Balance after T2 withdrawl: 980"

答案 3 :(得分:1)

这是一个并发问题

  1. T1检索1000
  2. T1将“之前”打印到系统输出1000
  3. T2检索1000
  4. T1撤回
  5. T1将“after”打印到system out 990
  6. T2将“之前”打印到系统输出1000
  7. T2撤回
  8. T2将“after”打印到system out 980。
  9. ...
  10. 无法按此顺序执行,但您明白了。为了让它像您想要的那样使用synchronized块。

      synchronized (acc) {
        System.out.println("Balance before " + Thread.currentThread().getName() + " withdrawl: " + acc.getBalance());
        w = acc.withdraw(amount);
        System.out.println("Balance after " + Thread.currentThread().getName() + " withdrawl: " + acc.getBalance());
      }