Mutithreading with System.out.format and System.ou

2020-03-26 12:35发布

I came across this example on Oracle's Java Tutorial describing Deadlock in multi threading scenarios.

So in this example I made following change at line 17 and line 18.

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) {
        //My Changes
        //System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); //Line 17
         System.out.println(this.name + ": " + bower.getName() + " has bowed to me!"); //Line 18
        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() {
        @Override
        public void run() {
            alphonse.bow(gaston);
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            gaston.bow(alphonse);
        }
    }).start();
  }
}

Upon doing these changes the program terminated successfully without causing deadlock and printed following output

Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed back to me!
Gaston: Alphonse has bowed to me!
Alphonse: Gaston has bowed back to me!

So my question is- why did it behave like this? How did the println statement prevent the deadlock?

4条回答
▲ chillily
2楼-- · 2020-03-26 12:40

To support the rest of the answers here with some actual proof I ran your code in a loop and it deadlocked 82 times out of 100 tries, so your code most definitely still deadlocks.

查看更多
何必那么认真
3楼-- · 2020-03-26 12:44

You are mixing up things here.

The fact that a piece of code can lead into a deadlock situation doesn't necessarily mean that you receive a deadlock each and any time that code runs.

That is one of the aspects that makes multi-threading such a hard topic: if you run your code once, or 10 times, or a 100 times, and everything "works"; it is still possible that it will fail the next time.

In other words: try putting that code in a loop on the outermost level, and sooner or later (probably sooner; if you don't do much "sleeping") you should hit the deadlock!

If things were that easy and deadlocks could be detected that easily, we wouldn't need all those books and libraries and ideas how to deal with multi-threading ...

查看更多
劳资没心,怎么记你
4楼-- · 2020-03-26 12:46

The deadlock is not dependent on the println function at all. It's caused by the two threads trying to access each other, and locking on each other.

The change from format to println will have introduced sufficient latency in the program to allow the threads to lock each other without colliding, i.e., deadlocking. So you haven't really fixed it; you've just added some latency that means the threads don't deadlock.

查看更多
唯我独甜
5楼-- · 2020-03-26 12:50

There is no difference in whether you use System.out.print or System.out.format: they're basically doing the same thing.

The deadlock occurs here if execution of Gaston.bow(Alphonse) is started between the start of Alphonse.bow(Gaston) and bower.bowBack(Alphonse) (or vice versa): the two threads are waiting for a monitor held by the other, and thus deadlock occurs.

This happens inconsistently, because it depends upon a subtle timing issue, depending upon how the threads are scheduled - it is possible that Alphonse.bow and bower.backBack(Alphonse) complete before Gaston.bow is executed, so it looks like there is no deadlock.

The classic way to fix this is to order the lock acquisition, so that the first the same lock is acquired first every time; this prevents the possibility of deadlock:

public void bow(Friend bower) {  // Method no longer synchronized.
  int firstHash = System.identityHashCode(this);
  int secondHash = System.identityHashCode(bower);

  Object firstMonitor = firstHash < secondHash ? this : bower;
  Object secondMonitor = firstHash < secondHash ? bower : this;
  synchronized (firstMonitor) {
    synchronized (secondMonitor) {
      // Code free (*) of deadlocks, with respect to this and bower at least.
    }
  }
}

(*) It's not quite guaranteed to be deadlock free, since System.identityHashCode can return the same value for distinct objects; but that's reasonably unlikely.

It's an application of the Birthday paradox: if you've got just two monitors, the chance of collision is something like 10^-18; but if you've got >77k monitors, a collision is more likely than not.

查看更多
登录 后发表回答