java - 加斯顿和阿尔方斯示例 : How does the bowBack get accessed?

标签 java anonymous-class

我一直对这个例子感到困惑。这似乎是不必要的复杂方式来展示它试图表达的概念:

http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

所以我的问题与同步或锁定无关;它可能与匿名类有关:

那么bow方法中调用的bowBack中的代码是如何访问的呢?实现 runnable 的匿名类被传递给 Bow() 方法。

也许这是一个糟糕的问题。但是,用匿名类来说明死锁会给示例带来不必要的复杂性,我这样说对吗?

最佳答案

第一个匿名内部类的 run 方法调用

alphonse.bow(gaston)

bow方法依次调用bower.bowBack(this),其效果是调用

gaston.bowBack(alphonse)

第二个匿名内部类的run方法调用

gaston.bow(alphonse)

最终调用

alphonse.bowBack(gaston)

这一切都不是很有趣,除非您考虑到两个 Runnable 实例由不同的线程运行,并且 bowbowBack code> 方法是同步的。因此我们会遇到以下情况:

  1. 第一个线程
    • 调用 alphonse.bow,锁定 alphonse
    • 调用 gaston.bowBack,锁定 Gaston
  2. 第二个线程
    • 调用 gaston.bow,锁定 Gaston
    • 调用 alphonse.bowBack,锁定 alphonse

或者,更简洁地说:

  1. 第一个线程锁定 alphonse,然后锁定 Gaston
  2. 第二个线程锁定 Gaston,然后锁定 alphonse

这很容易导致第一个线程获取了 alphonse 的锁,第二个线程获取了 Gaston 的锁,并且两个线程都无法继续,因为它正在等待获取另一个线程持有的锁。这就是本示例所说明的死锁。

现在您询问了匿名内部类。其中的一个要点是,有多个线程通过对象和锁进行交互。如果只有一个线程,这不会死锁。如何让代码在另一个线程上运行?最简单的方法是创建一个线程并向其传递一个 Runnable 实例,该实例的 run 方法在新创建的线程上执行。创建 Runnable 最简洁的方法(至少在 Java 8 之前)是使用匿名内部类。

匿名内部类的替代方案(同样是在 Java 8 之前)是使用命名类。这会给示例增加困惑,并且我认为这不会使其更容易理解。 (话又说回来,我对匿名内部类非常满意。)

Java 8 的替代方案是使用 lambda 代替匿名内部类:

new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();

这使得示例更加简洁,但如果您不熟悉 lambda,它可能没有帮助。

就目前情况而言,对于本示例来说,使用匿名内部类是让代码在不同线程上运行的合理方法。

很难想出有效的方法来使示例更简单。就目前情况而言,两个线程、两个对象和两个方法调用具有相当令人满意的对称性。主线程可能会对单个子线程产生死锁,但这会破坏对称性。调用不同对象上的单个方法也可能导致两个线程死锁。这将使示例更短,但可能会造成困惑,因为该单个方法将被调用四次次:从两个线程中的每一个线程,在两个对象中的每一个上调用一次。这可能更难理解,也更难解释。

关于java - 加斯顿和阿尔方斯示例 : How does the bowBack get accessed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24718676/

相关文章:

Java:将匿名类带到外面

java - 查找设备型号和品牌

java - JPA 和 EJB 中的错误

java - Cassandra 的节俭运输异常(exception)

java - 具有匿名类型构造函数的对象类

c# - 在 HtmlHelper 扩展方法中使用匿名对象

java - 生成的匿名类的 newInstance 上的 InstantiationException

java - Vaadin 中的 JPA 实体管理器生命周期模式

java - "Partially"POJO排序列表

java - 能否以某种方式限定最终参数以解决与匿名类成员的命名冲突?