多线程synchronized

使用synchronized同步线程,同一个账号取款的例子。在run方法中输出的金额有时候是不对的,如图,正常来说是900,800。但是多次运行发现有800,800的情况。
代码如下:

//同一账户同步取款
public class testSynchronized {
    public static void main(String[] args) {
        Account act = new Account("act-1", 1000);
        Thread t1 = new AccountThread(act);
        Thread t2 = new AccountThread(act);

        t1.setName("t1");
        t1.setName("t2");
        t1.start();
        t2.start();
    }
}

//取款线程
class AccountThread extends Thread {
    private Account act;
    //通过构造方法传递过来账户对象
    public AccountThread(Account account) {
        this.act = account;
    }

    @Override
    public void run() {
        Integer amount = 100;  //取款金额
        if (act.getBalance() > 0) {
            act.withdraw(amount);
            System.out.println("---" + act.getBalance());
        }
    }
}


//账户类
class Account {
    private String actno;   //账号
    private Integer balance;  //余额

    public Account(String actno, Integer balance) {
        this.actno = actno;
        this.balance = balance;
    }

    //取款方法
    public void withdraw(Integer amount) {
        synchronized (this) {//取款
            if (this.getBalance() == 0) {
                return;
            }
            Integer after = this.getBalance() - amount;
            this.setBalance(after);
            try {
                Thread.sleep(1000);
                System.out.println(this.getActno() + " 取款: " + amount + " ,余额: " + this.getBalance() + "  ---> " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public Integer getBalance() {
        return balance;
    }

    public void setBalance(Integer balance) {
        this.balance = balance;
    }
}


如下:

img

这是因为在代码中对同一个账户并发取款,导致了竞争条件。在AccountThread中,如果在两个线程取款的时候同时进入了withdraw方法,此时会发生余额被多次扣除,导致不正确的情况。

针对这个问题,你需要在Account的withdraw方法上加上synchronized关键字,来保证同一时刻只有一个线程在取款操作,从而避免竞争条件。

这是因为你的账户的初始余额是 1000,取款金额是 100,如果两个线程同时取款,就可能导致取款金额超过账户余额,所以输出不稳定。你可以通过添加一个条件判断来保证取款金额不能超过账户余额:

public void withdraw(Integer amount) {
synchronized (this) {//取款
if (this.getBalance() == 0 || this.getBalance() < amount) {
return;
}
Integer after = this.getBalance() - amount;
this.setBalance(after);
try {
Thread.sleep(1000);
System.out.println(this.getActno() + " 取款: " + amount + " ,余额: " + this.getBalance() + " ---> " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}