【问题标题】:Implementing bank account in Java用Java实现银行账户
【发布时间】:2011-05-18 13:11:39
【问题描述】:

我是 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();
    }
}

【问题讨论】:

    标签: java synchronization


    【解决方案1】:

    你没有做任何事情来同步你的 run 方法,所以提款前后的 printlns 和提款本身都不是原子的。你得到了线程交错。

    【讨论】:

    • 您已经同步了帐户(那里一切正常),但是如果您希望输出也以“不错”的顺序,那么您也必须同步它。但是一切都是正确的......当 T1 和 T2 退出时,帐户为 1000。他们退出后,T1 为 990(我猜更快),而 T2 为 980。
    【解决方案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 调用withdraw();
    5. T2 出金后余额不足;
    6. T1 余额不足;
    7. T1 调用withdraw();
    8. T1 出金后余额不足;

    【讨论】:

    • 没有可能。这就是正在发生的事情。 +1
    • 并非如此。另一种可能的情况是 7. 在 5. 之前。
    • 现在您已经编辑并添加了一个可能的场景,我将编辑我之前的评论。我的评论适用于您的原始回复“可能有一个线程在这些行之间调用撤消”。
    • 啊,好的。在我编辑 id 之前,我不知道你的评论出现了。我的错。
    • 没有错,帕特里克。仍然是一个很好的答案,我没有撤回我的 +1。欢迎来到 StackOverflow,顺便说一句。
    【解决方案3】:

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

        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"
    

    【讨论】:

      【解决方案4】:

      这是一个并发问题

      1. T1 检索到 1000
      2. T1 打印“之前”到系统输出 1000
      3. T2 检索到 1000
      4. T1 退出
      5. T1 打印“之后”到系统输出 990
      6. T2 打印“之前”到系统输出 1000
      7. T2 退出
      8. T2 将“之后”打印到系统输出 980。
      9. ...

      无法完全按照该顺序执行,但您明白了。为了让它像你想要的那样工作,请使用同步块。

        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());
        }
      

      【讨论】:

        猜你喜欢
        • 2015-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-27
        • 2014-10-10
        • 1970-01-01
        • 2014-10-09
        • 1970-01-01
        相关资源
        最近更新 更多