Gaston and Alphonse example: How does the bowBack get accessed?

天涯浪子 提交于 2019-12-25 01:28:27

问题


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:

  1. first thread
    • calls alphonse.bow, taking the lock on alphonse
    • calls gaston.bowBack, taking the lock on gaston
  2. second thread
    • calls gaston.bow, taking the lock on gaston
    • calls alphonse.bowBack, taking the lock on alphonse

Or, more concisely:

  1. first thread locks alphonse, then locks gaston
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!