0%

java | 转账问题

请看下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Account {
private int money;

public Account(int money) {
this.money = money;
}

public int getMoney() {
return money;
}

public void setMoney(int money) {
this.money = money;
}

public void transfer(Account target, int money) {
if (this.money > money) {
this.setMoney(this.getMoney() - money);
target.setMoney(target.getMoney() + money);
}
}
}

很明显在 tansfer 的时候会出现线程不安全问题。改成

1
2
3
4
5
6
public synchronized void transfer(Account target, int money) {
if (this.money > money) {
this.setMoney(this.getMoney() - money);
target.setMoney(target.getMoney() + money);
}
}

可以吗?

答案是不可以,因为,上面的代码相当于

1
2
3
4
5
6
7
8
public void transfer(Account target, int money) {
synchronized (this) {
if (this.money > money) {
this.setMoney(this.getMoney() - money);
target.setMoney(target.getMoney() + money);
}
}
}

synchronized (this) 只能让自身的变量加锁,并不能影响 target

这里有人有疑问,不是方法体内的都是原子操作吗,怎么还有线程不安全问题。

考虑这样一个场景:

当你代码执行到方法体内,这时候,时间片轮转到其他地方。那个地方,把 targetmoney 变了。当,时间片轮转回来,方法体内的 target 状态依然是之前的状态,而不是变化后的状态。所以,后面对 target 操作的时候,出现了错误。

所以要改成

1
2
3
4
5
6
7
8
public void transfer(Account target, int money) {
synchronized (Account.class) {
if (this.money > money) {
this.setMoney(this.getMoney() - money);
target.setMoney(target.getMoney() + money);
}
}
}

synchronized 可以想象成给房子加锁,锁只能锁住一个房子,但是,targetthis 属于两个房子。

请我喝杯咖啡吧~