请看下面的代码。
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
。
这里有人有疑问,不是方法体内的都是原子操作吗,怎么还有线程不安全问题。
考虑这样一个场景:
当你代码执行到方法体内,这时候,时间片轮转到其他地方。那个地方,把 target
的 money
变了。当,时间片轮转回来,方法体内的 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
可以想象成给房子加锁,锁只能锁住一个房子,但是,target
和 this
属于两个房子。