为什么需要在课堂上同步
我有以下示例,这些示例试图在2个帐户之间转移资金,我在想一个同步
就足够了,我几次运行了示例,发现总数不正确:
public class Test {
static Random random = new Random();
public static int randomAmount() {
return random.nextInt(100) + 1;
}
public static void main(String[] args) throws InterruptedException {
Account a = new Account(1000);
Account b = new Account(1000);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
a.transfer(b, randomAmount());
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
b.transfer(a, randomAmount());
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Total: " + (a.getBalance() + b.getBalance()));
}
}
class Account {
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public void transfer(Account target, int amount) {
synchronized (this) {
if (this.balance >= amount) {
this.setBalance(this.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
}
}
同步
on account.class.class
将解决此问题,因为只有一个线程可以随时输入Transfer
方法。但是,如果我只同步(this)
,我将无法弄清楚何时会导致多个线程导致不正确的总数。
I have the following example which tries to transfer money between 2 account, I was thinking a synchronized
on this
is enough, I ran the example several time and found the total is incorrect:
public class Test {
static Random random = new Random();
public static int randomAmount() {
return random.nextInt(100) + 1;
}
public static void main(String[] args) throws InterruptedException {
Account a = new Account(1000);
Account b = new Account(1000);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
a.transfer(b, randomAmount());
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
b.transfer(a, randomAmount());
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Total: " + (a.getBalance() + b.getBalance()));
}
}
class Account {
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public void transfer(Account target, int amount) {
synchronized (this) {
if (this.balance >= amount) {
this.setBalance(this.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
}
}
synchronized
on Account.class
will fix this since only one thread will be able to enter transfer
method at any time. But I am not able to figure out when multiple thread will cause the incorrect total if I only synchronized(this)
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
想象一下此情况:
使用两个线程:
线程X的调用
A.Tranfer(b,50)
将同步a
。线程Y的调用
B. Transfer(C,50)
将同步b
。请注意,它们正在同步不同的帐户,因此两个呼叫都可以同时执行同步块。
因此,各种种族条件可能发生。例如:
一个帐户有足够的钱时可以检查转移,但是到转移完成时,余额就已经消失了。从帐户的余额产生
否定为负。
金钱可能是“魔术”,因为
不是原子操作。特别是,另一个线程可以更改
getBalance()
和setBalance()
呼叫之间的平衡。另外,所需的发生在不存在各个线程之间的关系之前,因此在阅读
this.balance
时,线程可能会看到过时的值。锁定
account.class
是一种可能的解决方案。另一个可能的解决方案是在执行传输时锁定两个帐户。 (但是,如果您这样做,您需要防止僵局。)
Imagine this scenario:
with two threads:
Thread X's call to
a.tranfer(b, 50)
will synchronize ona
.Thread Y's call to
b.transfer(c, 50)
will synchronize onb
.Note that they are synchronizing on different accounts, so both calls can execute the synchronized block simultaneously.
So it is possible for various race conditions to occur. For example:
A transfer could be checked when one account has enough money, but by the time the transfer is done, the balance could already be gone. Resulting in the
from
account's balance going negative.Money could be "magic'd" into or out of existence because
is not an atomic operation. In particular, another thread could change the balance between the
getBalance()
andsetBalance()
calls.In addition, the required happens before relationships between the respective threads are not present, so it is possible that threads will see stale values when reading
this.balance
.Locking on
Account.class
is one possible solution.Another possible solution is to lock both accounts while performing a transfer. (But you need to guard against deadlocks, if you do it this way.)