Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java死锁问题_Java - Fatal编程技术网

Java死锁问题

Java死锁问题,java,Java,有人能解释一下为什么这段代码会死锁吗?谢谢 public class Deadlock { static class Friend { private final String name; public Friend(String name) { this.name = name; } public String getName() { return this.name;

有人能解释一下为什么这段代码会死锁吗?谢谢

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n", 
                    this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

下面是它可能的执行方式

  • 输入
    alphonse.bow(加斯顿),由于
    已同步
    关键字,alphonse现在已锁定
  • 输入
    gaston.bow(阿尔方斯),加斯顿现在已锁定
  • 无法执行
    bower.bowBack(此)从第一个
    bow
    方法调用开始,因为加斯顿(bower)已锁定。等待释放锁
  • 无法执行
    bower.bowBack(此)从第二个
    船头
    方法调用,因为阿尔方斯(船头)已锁定。等待释放锁

  • 两个线程都等待对方释放锁。

    请考虑以下事项:

    • 让Thread1
      run(){alphonse.bow(gaston);}
    • 让Thread2
      run(){gaston.bow(alphonse);}
    • 螺纹1进入阿尔方斯弓(加斯顿),锁定
      alphonse
      ,因为
      bow()
      同步的
    • 螺纹2进入加斯顿弓(阿尔方斯),锁定
      gaston
      ,因为
      bow()
      同步的
    • 在Thread1中,
      bower.bowBack(这个)计算为
      gaston.bowBack(阿尔方斯)
      
      • Thread1尝试获取当前由Thread2持有的gaston的锁
    • 在Thread2中,
      bower.bowBack(这个)评估为
      阿尔方斯弓背(加斯顿)
      
      • Thread2尝试获取当前由Thread1持有的
        alphonse
        的锁
    • 每个线程都在等待另一个线程释放锁,从而导致死锁
    问题是当前有过多的
    已同步
    。有很多方法可以“修复”这个问题;以下是一个有启发性的解决方案:

        public void bow(Friend bower) {
            synchronized (this) {
                System.out.format("%s: %s has bowed to me!%n", 
                        this.name, bower.getName());
            }
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
        }
    
    现在,使用
    synchronized(this)
    语句,
    bowBack()
    已完全
    synchronized
    ,但
    bow()
    仅部分
    已同步。这将防止僵局

    这里引用了《有效Java第二版》第67项:避免过度同步

    为避免活动性和安全性故障,切勿在
    同步的
    方法或块内将控制权让与客户端。换句话说,在
    synchronized
    区域内,不要调用设计为重写的方法,也不要调用客户机以函数对象的形式提供的方法。从
    synchronized
    区域的角度来看,这些方法是不同的。这个类不知道这个方法做什么,也不能控制它。根据外来方法的功能,从
    synchronized
    区域调用它可能会导致异常、死锁或数据损坏

    […]通常,您应该在
    同步的
    区域内尽可能少做工作。获取锁,检查共享数据,必要时对其进行转换,然后放下锁

    本质上,
    bower.bowBack(this)
    是试图将控制权让给一个外来方法,因为
    bowBack()
    不是
    类中的
    final
    方法。考虑下面的尝试来解决这个问题,例如:

        // attempt to fix: STILL BROKEN!!!
    
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
            // ceding control to alien method within synchronized block!
        }
        
        // not a final method, subclasses may @Override
        public void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
        }
    
    上述代码不会与当前的
    alphonse/gaston
    场景死锁,但由于
    bow()
    将控制权让给非
    最终的
    方法
    bowBack()
    ,子类可以
    @重写该方法,从而导致
    bow()
    死锁。也就是说,
    bowBack()
    bow()
    的外来方法,因此不应该从
    同步的
    区域中调用

    工具书类
    另见
    • 有效Java第二版
      • 项目66:同步访问共享可变数据
      • 项目15:尽量减少可变性

    最好的理解方法是在调用bower.bowBack之前将下面的代码放在bow()中

    try {
    Thread.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    

    这并不总是会导致死锁。如果线程2在线程1完成后启动,那么就不会出现死锁。在调用bower.bowBack()方法之前,两个线程启动之间的Thread.sleep避免了in bow()方法的死锁