2016-08-06 28 views
1

为什么在我的代码中有一个竞争条件,当所有帐户同步?怎么会有竞争条件?

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锁定其fromAccount(托马斯)和transfer2锁定其fromAccount(马蒂亚斯),所以不应该他们都在僵局结束了?

+1

是否有死锁或竞态条件?你提到了两者,但它们是不同的。看起来像你有一个潜在的种族,但没有一个快速浏览僵局。 – Brick

回答

1

问题是代码toAccount.book(amount)不能与同步保护一起运行。

因此从技术上讲是可能发生的线程1上thomasAccount持有锁及线程持有锁mathiasAccount但仍然线程2在同一时间运行书thomasAccount作为线程1上运行thomasAccount书。这可能会导致不一致,因为一个线程可以忽略第二个线程的结果。

只要在任何帐户上运行的任何线程都必须首先锁定(同步)帐户,而不管它是正负。

为避免死锁,请使帐户具有可比性(或使用帐户的某个ID)并始终按升序锁定帐户。或者你可以使用散列,但如果散列相同,那么你需要一些全局锁。

+0

感谢您的回答,但我仍然不完全明白一件事: 如果thread1锁定'thomasAccount',thread2如何在'thomasAccount'上运行book? – Numb3rs

+0

只有当它尝试获取相同的锁定时,thread2才会停止。调用'book()'本身不会执行锁定。 –

+0

再次感谢你,你真的帮助我在这里! 因此,如果我理解正确,锁定的对象仍然可以从其他线程访问,例如通过方法调用,只要它不试图获取锁定。是对的吗? – Numb3rs

1

您的run方法仅在fromAccount上同步,而不是toAccount。未同步的代码不会被同步代码阻止;试图访问某些东西的两个线程必须两个同步它以便序列化访问。

所以你run方法不能同步只是fromAccount,而且对toAccount,为了使任何同步上toAccount使其等待。