2011-08-23 44 views
5

我试图写的很简单的程序,它会模仿简单的僵局,在那里线程A用于资源A的锁定等待由线程B和线程B等待资源B线程A锁定Java的新手 - 死锁仿

这里是我的代码:

//it will be my Shared resource 
public class Account { 
    private float amount; 

    public void debit(double amount){ 
     this.amount-=amount; 
    } 

    public void credit(double amount){ 
     this.amount+=amount; 
    } 

} 

这是我可以运行它上面的资源上进行操作:

public class BankTransaction implements Runnable { 
    Account fromAccount,toAccount; 
    float ammount; 
    public BankTransaction(Account fromAccount, Account toAccount,float ammount){ 
     this.fromAccount = fromAccount; 
     this.toAccount = toAccount; 
     this.ammount = ammount; 
    } 

    private void transferMoney(){ 
     synchronized(fromAccount){ 
      synchronized(toAccount){ 
       fromAccount.debit(ammount); 
       toAccount.credit(ammount); 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e) { 
       e.printStackTrace(); 
       } 
       System.out.println("Current Transaction Completed!!!"); 
      } 
     } 
    } 

    @Override 
    public void run() { 
     transferMoney(); 
    } 

} 

,最后我的主类:

public static void main(String[] args) { 

    Account a = new Account(); 
    Account b = new Account(); 
    Thread thread1 = new Thread(new BankTransaction(a,b,500)); 

    Thread thread2 = new Thread(new BankTransaction(b,a,500)); 
     thread1.start(); 
     thread2.start(); 
System.out.println("Transactions Completed!!!"); 

    } 
} 

为什么此代码运行成功执行,我没有和deadLock?

回答

10

它有潜在的死锁 - 但两个锁都很快就被一起获得,一个线程可以在另一个线程获得它的第一个之前得到两个。

把两个同步语句之间的另一个Thread.sleep(500);呼叫,它确实死锁:两个线程都将进入“自己的”锁外,睡眠,然后醒来时他们都会发现,他们的“内部”锁已经收购。

这是由于您的同步语句是反对称的:对于一个线程,外部同步锁是另一个线程的内部同步锁,反之亦然。

+0

睡眠可以引入非确定性。你可以使它具有确定性;请参阅下面的答案。 – Toby

+0

@Toby:是的,你可以使它成为确定性的 - 但是'sleep'是显示死锁*可能发生的一种好方法,因为你总能想象一个没有睡眠的程序执行类似于*使用睡眠的程序,只是凭借未安排的线程。换句话说,当你插入睡眠时失败的程序本质上是有缺陷的,因为你可能在现实生活中看到“意外”的相同。插入通知也是如此。 –

5

其中一个线程可能会输入两个​​部分,完全阻塞另一个线程直到完成。

4

您需要模拟'不幸的时机'。尝试在两个锁之间添加睡眠:

synchronized(fromAccount){ 
    Thread.sleep(2000); 
    synchronized(toAccount){ 
0

死锁的原因是线程A正在等待线程B在A继续之前释放一些资源;线程B一样,它不会继续,直到线程A释放一些资源。换句话说,A和B永远等待对方。

在代码片段中,synchronized可以阻塞其他线程,因为此时只有一个线程可以执行该块。 thread.sleep()暂停500毫秒的线程,然后继续。永远相互等待的条件不会满足,为什么它不是僵局。

下面的代码片段是一个很好的例子来说明僵局

public class threadTest{ 

    public class thread1 implements Runnable{ 
     private Thread _th2; 
     private int _foo; 

     public thread1(Thread th2){}; 
     public void run(){ 
     for(int i = 0; i<100; i++){foo += foo;}; 
     synchronized(this){this.notify()}; 
     synchronized(_th2){ 
      _th2.wait(); 
      _foo += _th2.foo; 
      System.out.print(" final result " + _foo); 
     } 
     } 
    } 

    public class thread2 implements Runnable{ 
     private final thread1 _th1; private int _foo; 
     public thread2(thread1 th1){}; 
     public void Run(){ 
      synchronized(_th1){_th1.wait()}; 
      synchronized(this){ 
      _foo += th1._foo(); 
      this.notify();     
      } 
     } 
    } 
    } 
} 

//忽略访问类

因为没有机制保证两个线程的执行顺序私有变量的方式,很可能线程2不会从thread1接收到最近启动的通知,因此它会在继续执行之前等待通知。与thread1相同,它不能执行下一个执行,直到它收到来自thread2的通知。他们两个永远等待对方,典型的僵局。

2

由Jon提出的睡眠可以引入非确定性,您可以使用某个协调器(如锁存器)来确定它。为了澄清,我把它看作一个测试问题:如何证明每次都陷入僵局,这可能不是你想要的。

看到这个code为一个例子,blog post这样描述一点。