当所有帐户同步时,为什么代码中会出现竞争条件?
class Transfer implements Runnable {
Konto fromAccount;
Konto toAccount;
Integer amount;
public void run() {
synchronized (fromAccount) {
if (fromAccount.book(-amount)) {
toAccount.book(amount);
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException
Account thomas = new Account(1234, 100);
Account mathias = new Account(5678, 100);
Thread transfer1 = new Thread(new Transfer(80, thomas, mathias));
Thread transfer2 = new Thread(new Transfer(95, mathias, thomas));
transfer1.start();
transfer2.start();
transfer1.join();
transfer2.join();
}
根据我的理解,transfer1将其锁定来自帐户(托马斯),而transfer2将其锁定来自帐户(mathias),所以他们都不应该陷入死锁?
答案 0 :(得分:1)
问题是代码toAccount.book(amount)
没有使用同步保护运行。
从技术上讲,可能会发生thread1对thomasAccount
保持锁定而thread2对mathiasAccount
保持锁定但是thread2仍然在thomasAccount
上运行book,同时thread1运行book on thomasAccount
。这可能导致不一致,因为其中一个线程可以忽略第二个线程的结果。
任何帐户上运行的任何线程都必须首先锁定(同步)该帐户,无论它是加号还是减号。
为避免死锁,请使帐户具有可比性(或使用帐户的某些ID),并始终按升序锁定帐户。或者您可以使用散列,但是如果散列相同,则需要一些全局锁定。
答案 1 :(得分:1)
您的run
方法仅在fromAccount
上同步,而不是toAccount
。同步代码不会阻止未同步的代码;尝试访问某些内容的两个线程必须两者同步才能序列化访问。
因此,您的run
方法不仅要在fromAccount
上同步,还要在toAccount
上进行同步,以便在toAccount
上进行任何同步,使其等待。