Подтвердить что ты не робот

Тупик - как это происходит в этом примере?

может кто-нибудь объяснить:

  • почему мы зашли в тупик?
  • Как Gaston может войти в функциональный банд до выхода Alphonse из этой функции? (он должен вернуться из функции bowBack(), чтобы выйти из функции bow() - или)?

Это результат, который я получаю - и тогда программа застревает!

Альфонс: Гастон поклонился мне!

Гастон: Альфонс поклонился мне!

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) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            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() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
} 
4b9b3361

Ответ 1

Блок/метод synchronized синхронизируется с this, то есть экземпляр объекта, вызываемый блоком/методом. (Для static "экземпляр объекта" следует заменить на "экземпляр класса".)

Это ваши 2 объекта синхронизируются с самим собой, а не с обычным объектом.

Попробуйте что-то вроде этого:

public class Deadlock {
   static class Friend {
      private final String name;
      public Friend(String name) {
         this.name = name;
      }
      public String getName() {
         return this.name;
      }
      public void bow(Friend bower) {
         synchronized (getClass()) {
            System.out.format("%s: %s  has bowed to me!%n", this.name, bower.getName());
            bower.bowBack(this);
         }
      }
      public void bowBack(Friend bower) {
         synchronized (getClass()) {
            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() {
         public void run() { alphonse.bow(gaston); }
      }).start();
      new Thread(new Runnable() {
         public void run() { gaston.bow(alphonse); }
      }).start();
   }
}

Ответ 2

Тема 1: alphonse экземпляр блокируется из alphonse.bow(gaston);, который печатает строку, а затем вызывает gaston.bowBack() (но gaston заблокирован из Thread 2 из-за синхронизированного экземпляра bow(), вызываемого на нем ниже)

Тема 2: gaston экземпляр блокируется из gaston.bow(alphonse);, который печатает строку, а затем вызывает alphonse.bowBack() (но alphonse заблокирован из Thread 1 из-за вызванного им синхронизированного экземпляра bow())

Итак, они оба ждут релиза и не могут выйти из метода bow(), поэтому Deadlock

Ответ 3

Прежде всего, использование синхронизировано неправильно. Оракул tutorial красиво заявляет:

Во-первых, невозможно, чтобы два вызова синхронизированных методов в объекте одинаковый перемещались.

Как объясняется другим ответом: код, показанный в примере , не использует "общую блокировку" (синхронизированные методы на двух разных объектах не влияют на вызов "другого" метода).

Помимо этого: как только вы удалите те System.out.format() вызовы - ваша программа может (чаще всего) не заходить в тупик.

Или: поместите println() в свой основной до, вы запустите потоки - опять же, в тупике будет не.

Другими словами: печать на консоль занимает очень много времени. Поэтому это сильно влияет на время ваших потоков! Что здесь происходит, так это то, что большую часть времени тратится на эти выходные действия консоли. См. здесь для аналогичного вопроса, который даже использует те же имена; -)

Ответ 4

Что происходит в вашем примере:

  • Thread Alphonse получает блокировку для Alphonse, введя функцию bow.

  • Тема Gaston приобретает замок Gaston, введя функцию bow.

  • Thread Alphonse запрашивает блокировку Gaston для входа в функцию bowBack, но эта блокировка в настоящее время поддерживается Thread Gaston, поэтому Alphonse вынужден ждать.

  • Thread Gaston запрашивает блокировку Alphonse для входа в функцию bowBack, но эта блокировка в настоящее время поддерживается Thread Alphonse, поэтому Gaston вынужден ждать.

тупиковой.

Почему это происходит:

Функция A synchronized является синтаксическим сахаром для synchronized(this) { ... } Таким образом, класс выше также может быть записан следующим образом:

public void bow(Friend bower) {
    synchronized (this) {
        System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
        bower.bowBack(this);
    }
}

public void bowBack(Friend bower) {
    synchronized (this) {
        System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName());
    }
}

this в этом примере, однако, является экземпляром класса, поэтому каждый экземпляр имеет индивидуальный замок. Если вы хотите заблокировать один и тот же объект во всех экземплярах класса, вам необходимо заблокировать статический объект следующим образом:

protected static final Object STATIC_LOCK = new Object();

public void bow(Friend bower) {
    synchronized (STATIC_LOCK) {
        System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
        bower.bowBack(this);
    }
}

public void bowBack(Friend bower) {
    synchronized (STATIC_LOCK) {
        System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName());
    }
}

Поскольку этот объект LOCK статичен, оба потока теперь будут блокироваться на одном и том же объекте и, следовательно, правильно блокируют друг друга. Обратите внимание на ключевое слово final, которое настоятельно рекомендуется в этом случае, поскольку в противном случае синхронизированные блокировки могут меняться во время выполнения (по ошибкам или недочетам в вашем коде), что вернет вас в ситуацию с мертвой блокировкой по той же причине как указано выше.