问题
I have always had trouble with this example. It seems unnecessarily complex way to show the concepts it is trying to:
http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
So my question has nothing to do with synchronization or locking; it is probably about anonymous classes:
It is simply how does the code in bowBack which is called in the bow method get accessed? The anonymous class that implements runnable is passed the bow() method.
Maybe this is a bad question. But am I right that illustrating deadlock with anonymous classes introduces complexity into the example that is not needed?
回答1:
The first anonymous inner class's run
method calls
alphonse.bow(gaston)
The bow
method in turn calls bower.bowBack(this)
which has the effect of calling
gaston.bowBack(alphonse)
The second anonymous inner class's run
method calls
gaston.bow(alphonse)
which ends up calling
alphonse.bowBack(gaston)
None of this is very interesting until you take into account that the two Runnable
instances are being run by different threads and that the bow
and bowBack
methods are synchronized. Thus we have a situation where the following occurs:
- first thread
- calls
alphonse.bow
, taking the lock on alphonse - calls
gaston.bowBack
, taking the lock on gaston
- calls
- second thread
- calls
gaston.bow
, taking the lock on gaston - calls
alphonse.bowBack
, taking the lock on alphonse
- calls
Or, more concisely:
- first thread locks alphonse, then locks gaston
- second thread locks gaston, then locks alphonse
This easily leads to a situation where the first thread has taken the lock on alphonse and the second thread has taken the lock on gaston, and neither thread can proceed because it's waiting to take a lock held by the other thread. That's the deadlock this example is illustrating.
Now you asked about the anonymous inner classes. An essential point of this is that there are multiple threads interacting over objects and locks. If there were only a single thread, this wouldn't deadlock. How does one get code to run on another thread? The easiest way is to create a thread and pass it an instance of a Runnable
whose run
method is executed on the newly created thread. The most concise way to create a Runnable
(at least prior to Java 8) is to use an anonymous inner class.
An alternative to an anonymous inner class (again, prior to Java 8) would be to use a named class. This would add clutter to the example and I don't think it would make it more understandable. (Then again, I'm quite comfortable with anonymous inner classes.)
A Java 8 alternative would be to use lambdas instead of anonymous inner classes:
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
This makes the example quite a bit more concise, though it probably isn't helpful if you are unfamiliar with lambdas.
As it stands, using anonymous inner classes is a reasonable way for this example to get code running on the different threads.
It's hard to think of effective ways to make the example simpler. As it stands, the two threads, two objects, and two method calls have a fairly pleasing symmetry. It would be possible to have the main thread deadlock against a single child thread, but that would break the symmetry. It would also be possible to have the two threads deadlock calling a single method on the different objects. That would make the example shorter, but it would potentially be confusing because that single method would be called four times: once on each of two objects, from each of two threads. It might be harder to follow and harder to explain.
回答2:
The bow
method calls bowback
; is that what you mean? You ask "how does the code in bowback which is called in the bow method get accessed", I'm not sure what else you would mean...
回答3:
For me it was a lot easier to understand when I removed the bowBack
method and rewrote the bow
method in this way:
public void bow(Friend bower) {
synchronized (this) {
System.out.format("%s: %s"
+ " has bowed to me!%n",
this.name, bower.getName());
synchronized (bower) {
System.out.format("%s: %s"
+ " has bowed back to me!%n",
this.name, bower.getName());
}
}
}
the basic structure is this:
public void bow(Friend bower) {
synchronized (this) {
synchronized (bower) {
}
}
}
So, you get:
public void bow(Friend bower) {
synchronized (alphonse) {
synchronized (gaston) {
}
}
}
and
public void bow(Friend bower) {
synchronized (gaston) {
synchronized (alphonse) {
}
}
}
The black arrows show what waits on what.
来源:https://stackoverflow.com/questions/24718676/gaston-and-alphonse-example-how-does-the-bowback-get-accessed