Jef*_*eff 0 java anonymous-class
我一直对这个例子有问题。显示它试图展示的概念似乎是一种不必要的复杂方式:
http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
所以我的问题与同步或锁定无关;它可能与匿名类有关:
只是如何访问在 bow 方法中调用的 bowBack 中的代码?实现 runnable 的匿名类被传递给 bow() 方法。
也许这是一个不好的问题。但是,使用匿名类来说明死锁会在不需要的示例中引入复杂性,我对吗?
第一个匿名内部类的run方法调用
alphonse.bow(gaston)
Run Code Online (Sandbox Code Playgroud)
该bow方法依次调用bower.bowBack(this)具有调用效果的方法
gaston.bowBack(alphonse)
Run Code Online (Sandbox Code Playgroud)
第二个匿名内部类的run方法调用
gaston.bow(alphonse)
Run Code Online (Sandbox Code Playgroud)
最终调用
alphonse.bowBack(gaston)
Run Code Online (Sandbox Code Playgroud)
除非您考虑到两个Runnable实例由不同的线程运行并且bow和bowBack方法是同步的,否则这些都不是很有趣。因此,我们会遇到以下情况:
alphonse.bow,锁定阿尔方斯gaston.bowBack,锁定加斯顿gaston.bow,锁定加斯顿alphonse.bowBack,锁定阿尔方斯或者,更简洁地说:
这很容易导致第一个线程在 alphonse 上获得锁而第二个线程在 gaston 上获得锁,并且两个线程都无法继续,因为它正在等待获得另一个线程持有的锁。这就是这个例子说明的僵局。
现在您询问了匿名内部类。一个要点是有多个线程在对象和锁上进行交互。如果只有一个线程,这不会死锁。如何让代码在另一个线程上运行?最简单的方法是创建一个线程并将Runnable其run方法在新创建的线程上执行的实例传递给它。创建一个Runnable(至少在 Java 8 之前)最简洁的方法是使用匿名内部类。
匿名内部类(同样在 Java 8 之前)的替代方法是使用命名类。这会给示例添加混乱,我认为这不会使它更易于理解。(再说一次,我对匿名内部类很满意。)
Java 8 的替代方法是使用 lambdas 而不是匿名内部类:
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
Run Code Online (Sandbox Code Playgroud)
这使示例更加简洁,但如果您不熟悉 lambda,它可能没有帮助。
就目前而言,使用匿名内部类是本示例在不同线程上运行代码的合理方法。
很难想出有效的方法来使示例更简单。就目前而言,两个线程、两个对象和两个方法调用具有相当令人满意的对称性。主线程可能会针对单个子线程发生死锁,但这会破坏对称性。也有可能让两个线程死锁,在不同的对象上调用一个方法。这将使示例更短,但它可能会令人困惑,因为该单个方法将被调用四次:在两个对象中的每一个上调用一次,来自两个线程中的每一个。可能更难理解,也更难解释。