【问题标题】:Java for newbies - DeadLock imitationJava 新手 - 死锁模仿
【发布时间】:2013-12-09 20:47:30
【问题描述】:

我正在尝试编写一个非常简单的程序来模仿简单的死锁,其中线程 A 等待被线程 B 锁定的资源 A,而线程 B 等待被线程 A 锁定的资源 B。

这是我的代码:

//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?

【问题讨论】:

    标签: java multithreading concurrency deadlock


    【解决方案1】:

    它有潜在的死锁 - 但是两个锁一起被获取的速度非常快,以至于一个线程可以在另一个线程有机会获取它的第一个之前得到两个。

    在两个同步语句之间再调用一个Thread.sleep(500);,它确实死锁:两个线程都将进入“他们的”外部锁,休眠,然后当他们醒来时,他们都会发现他们的“内部”锁已被获取。

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

    【讨论】:

    • 睡眠会引入不确定性。您可以使其具有确定性;请参阅下面的答案。
    • @Toby:是的,你可以让它成为确定性的——但sleep 是展示死锁可能如何发生的好方法,因为你总是可以想象一个没有sleeps 的执行与 确实 使用 sleeps 的执行方式类似,只是因为线程没有被调度。换句话说,当您插入 sleep 时失败的程序本质上是有缺陷的,因为您可能会在现实生活中“意外”看到相同的内容。插入通知也不一样。
    【解决方案2】:

    其中一个线程可能会进入 both synchronized 部分,完全阻塞另一个线程直到它完成。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        上面 Jon 建议的睡眠可能会引入非确定性,您可以使用诸如闩锁之类的协调器来使其具有确定性。不过要澄清一下,我将其视为一个测试问题:如何每次都证明死锁,而这可能不是您想要的。

        请参阅此 code 以获取示例,并查看 blog post 对其进行一些描述。

        【讨论】:

          【解决方案5】:

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

          在sn-p代码中,synchronized可以暂时阻塞其他线程,因为此时只有一个线程可以执行该阻塞。 thread.sleep() 将线程挂起 500 毫秒,然后继续。永远相互等待的条件不满足,所以不是死锁。

          下面的 sn-p 是一个很好的例子来说明死锁

          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的通知才能执行下一次执行。他们俩永远互相等待,典型的僵局。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-28
            • 2020-04-03
            • 1970-01-01
            • 1970-01-01
            • 2017-03-31
            • 1970-01-01
            相关资源
            最近更新 更多