2016-10-25 10 views
2

Я столкнулся с этим example в учебнике Java по Java, в котором описывается Deadlock в многопоточных сценариях.Mutithreading с System.out.format и System.out.println

Таким образом, в этом примере я сделал следующие изменения в линии 17 и линии 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(); 
    } 
} 

После выполнения этих изменений программы успешно завершено, не вызывая затор и распечатан следующим выходных

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! 

Так мой Вопрос в том, почему он так себя ведет? Как заявление println предотвратило тупик?

+0

Я не вижу, как это будет. 'System.out.format' на самом деле ничего не отличается от' System.out.println' относительно блокировки. –

+2

Не имеет значения - дело в том, что тупик зависит от чередования потоков, которые будут отличаться между прогонами. Если вы запускаете его несколько раз с помощью System.format, вы, вероятно, время от времени будете наблюдать за правильным результатом. И с помощью println вы также увидите прогоны, где программа выполняет тупик. – assylias

+0

Действительно: первый вариант [отлично работает на ideone] (http://ideone.com/bV6nd8). –

ответ

5

Нет никакой разницы в том, используете ли вы System.out.print или System.out.format: они в основном делают то же самое.

Тупик происходит здесь, если выполнение Gaston.bow(Alphonse) начинается между началом Alphonse.bow(Gaston) и bower.bowBack(Alphonse) (или наоборот): две нити ждут монитора, проведенного с другой стороны, и, таким образом, происходит тупиковый.

Это происходит непоследовательно, потому что это зависит от тонкой проблемы синхронизации, в зависимости от того, как потоки распределены - вполне возможно, что Alphonse.bow и bower.backBack(Alphonse) завершено до Gaston.bow выполняются, так что похоже, нет тупикового.

Классический способ исправить это, чтобы заказать захват блокировки, чтобы первый тот же замок был приобретен первым каждый раз; это предотвращает возможность тупиковой ситуации:

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. 
    } 
    } 
} 

(*) Это не совсем гарантированно тупиковой бесплатно, так как System.identityHashCode может возвращать одинаковое значение для различных объектов; но это вряд ли возможно.

Это приложение Birthday paradox: если у вас есть только два монитора, вероятность столкновения - это что-то вроде 10^-18; но если у вас есть> 77k мониторы, вероятность столкновения более вероятна.

4

Вы смешиваете вещи здесь.

Тот факт, что часть кода может привести в тупиковой ситуации не обязательно означает, что вы получите тупиковой ситуации каждый и в любое время, что код работает.

Это один из аспектов, который делает многопоточность такой сложной темой: если вы запускаете свой код один раз или 10 раз или 100 раз, и все «работает»; все еще возможно, что он будет терпеть неудачу в следующий раз.

Другими словами: попробуйте поместить этот код в петлю на крайнем уровне, и рано или поздно (скорее всего, скорее, если вы не сделаете много «спящего»), вы должны зайти в тупик!

Если бы все было так легко и тупиков можно обнаружить, что легко, мы не нужны все эти книги и библиотеки и идеи, как иметь дело с многопоточности ...

1

Тупик это не зависит от println функция вообще. Это вызвано тем, что два потока пытаются получить доступ друг к другу и блокируются друг на друга.

Изменение формата в println обеспечит достаточную задержку в программе, чтобы потоки могли блокировать друг друга, не сталкиваясь, т. Е. Блокировать. Таким образом, вы на самом деле не исправили это; вы только что добавили некоторую задержку, что означает, что потоки не блокируются.

3

Чтобы поддержать остальные ответы здесь с некоторыми фактическими доказательствами, я запустил ваш код в цикле, и он зашел в тупик 82 раза из 100 попыток, поэтому ваш код определенно все еще блокирует.

+0

Я не согласен с этим. Вопрос: «Как заявление println предотвратило тупик?» и мой ответ в том, что это не так. Я могу согласиться с тем, что он не является ответом сам по себе, хотя, поскольку я ссылаюсь на другие ответы – Raniz

+0

Имеет смысл ;-) – GhostCat

 Смежные вопросы

  • Нет связанных вопросов^_^