Java死锁问题

2024-03-31

谁能解释一下为什么这段代码出现死锁。谢谢

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();
    }
}

考虑以下:

  • Let Thread1 run() { alphonse.bow(gaston); }
  • Let Thread2 run() { gaston.bow(alphonse); }
  • Thread1 enters alphonse.bow(gaston);, 锁定alphonse since bow() is synchronized
  • Thread2 enters gaston.bow(alphonse);, 锁定gaston since bow() is synchronized
  • In Thread1, bower.bowBack(this); evaluates to gaston.bowBack(alphonse);
    • Thread1尝试获取锁gaston,目前由Thread2
  • In Thread2, bower.bowBack(this); evaluates to alphonse.bowBack(gaston);
    • Thread2尝试获取锁alphonse,目前由Thread1
  • 每个线程都在等待另一个线程释放锁,因此出现死锁

问题是存在过多synchronized现在。有很多方法可以“解决”这个问题;这是一个有指导意义的解决方案:

    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());
    }

Now bowBack()是完全synchronized, but bow()只是synchronized部分地,使用synchronized(this)陈述。这将防止僵局。

以下是来自的引述《Effective Java》第二版,第 67 条:避免过度同步

为了避免活性和安全故障,绝不将控制权交给客户在一个synchronized方法或块。换句话说,在一个synchronized区域,不要调用设计为被重写的方法,或由客户端以函数对象的形式提供的方法。从classsynchronized区域,此类方法是alien。该类不知道该方法的作用,也无法控制它。根据外来方法的作用,从synchronized区域可能会导致异常、死锁或数据损坏。

[...] 作为一项规则,您应该在内部做尽可能少的工作synchronized地区。获取锁,检查共享数据,必要时对其进行转换,然后删除锁。

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

    // 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()将控制权让给非final method bowBack(), 子类可以@Override该方法会导致bow()陷入僵局。那是,bowBack() is an alien方法bow(),因此应该NOT已从内部调用synchronized region.

参考

  • JLS 8.4.3.6synchronized Methods http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.3.6
  • JLS 14.19synchronized陈述 http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.19
  • JLS 17.1 锁 http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.1

See also

  • Effective Java 2nd Edition
    • 第 66 项:同步对共享可变数据的访问
    • 第 15 项:最小化可变性
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java死锁问题 的相关文章

随机推荐