Вопрос о блокировке Java

может кто-нибудь объяснить мне, почему в этом коде есть тупик. Спасибо

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(); } } 

Вот как это, вероятно, будет выполнено.

  1. Введите alphonse.bow(gaston); , теперь привязка теперь заблокирована из-за synchronized ключевого слова
  2. Введите gaston.bow(alphonse); , газон теперь заблокирован
  3. Невозможно выполнить bower.bowBack(this); от первого вызова метода bow потому что газон (беседка) заблокирован. Подождите, пока блокировка не будет выпущена.
  4. Невозможно выполнить bower.bowBack(this); от вызова второго bow потому что альфа-сигнал (беседка) заблокирован. Подождите, пока блокировка не будет выпущена.

Оба streamа ждут друг друга, чтобы освободить блокировку.

Рассмотрим следующее:

  • Пусть Thread1 run() { alphonse.bow(gaston); } run() { alphonse.bow(gaston); }
  • Пусть Thread2 run() { gaston.bow(alphonse); } run() { gaston.bow(alphonse); }
  • Thread1 вводит alphonse.bow(gaston); , блокировка alphonse с synchronized bow()
  • Thread2 входит в gaston.bow(alphonse); , запирающий gaston с synchronized bow()
  • В Thread1 , bower.bowBack(this); оценивает gaston.bowBack(alphonse);
    • Thread1 пытается получить блокировку для gaston , в настоящее время удерживаемую Thread2
  • В Thread2 , bower.bowBack(this); оценивается в alphonse.bowBack(gaston);
    • Thread2 пытается получить блокировку для alphonse , в настоящее время удерживаемую Thread1
  • каждый stream ждет другого, чтобы освободить блокировку, следовательно, тупик

Проблема в том, что в настоящее время происходит чрезмерная synchronized . Существует много способов «исправить» это; вот поучительное решение:

  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 synchronized void bowBack(Friend bower) { System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName()); } 

Теперь bowBack() полностью synchronized , но bow() частично synchronized , используя synchronized(this) оператор. Это предотвратит тупик.

Вот цитаты из Effective Java 2nd Edition, пункт 67: Избегайте чрезмерной синхронизации

Чтобы избежать сбоев в работе и безопасности, никогда не уступайте управление клиенту в рамках synchronized метода или блока. Другими словами, внутри synchronized области не вызывать метод, предназначенный для переопределения или предоставляемый клиентом в виде функционального объекта. С точки зрения class с synchronized областью такие методы чужды . Класс не знает, что делает этот метод и не контролирует его. В зависимости от того, что делает инопланетный метод, вызов его из synchronized области может вызвать исключения, взаимоблокировки или повреждение данных.

[…] Как правило, вы должны делать как можно меньше работы внутри synchronized регионов. Получите блокировку, проверьте общие данные, при необходимости измените их и отпустите блокировку.

В сущности, bower.bowBack(this) является попыткой уступить контроль над чужим методом, потому что bowBack() не является final методом в class Friend . Рассмотрим следующую попытку исправить проблему, например:

  // attempt to fix: STILL BROKEN!!! public synchronized void bow(Friend bower) { System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName()); bower.bowBack(this); // ceding control to alien method within synchronized block! } // not a final method, subclasses may @Override public void bowBack(Friend bower) { System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName()); } 

Вышеприведенный код не будет alphonse/gaston сценарием alphonse/gaston , но так как bow() уступает управление bowBack() методу bowBack() , подclass может @Override метод таким образом, который вызовет тупик bow() , То есть, bowBack() – это метод инопланетянины для bow() , и поэтому НЕ должен быть вызван из synchronized области.

Рекомендации

  • JLS 8.4.3.6 synchronized методы
  • JLS 14.19 synchronized заявление
  • JLS 17.1 Замки

Смотрите также

  • Эффективное Java 2nd Edition
    • Пункт 66: Синхронизация доступа к общим изменяемым данным
    • Пункт 15: Минимизировать изменчивость

Лучший способ понять – поставить ниже код в лук () перед вызовом bower.bowBack

 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } 
  • Java BigDecimal.power (BigDecimal exponent): Есть ли библиотека Java, которая это делает?
  • Как подключиться через HTTPS с помощью Jsoup?
  • Изменение размера изображения до полной ширины и фиксированной высоты с помощью Picasso
  • Как исправить INSTALL_PARSE_FAILED_MANIFEST_MALFORMED в приложении для Android
  • GSON выбрасывает «Ожидаемый BEGIN_OBJECT, но был BEGIN_ARRAY»?
  • Android Studio не удалось найти версию, совместимую с com.android.support:appcompat-v7:+
  • Трансляция, когда состояние сети изменилось
  • Android - настройка тайм-аута для AsyncTask?
  • Firestore - объект с внутренним объектом
  • Как нарисовать круг на canvasе в Android?
  • Как обрезать изображение на Java?
  • Давайте будем гением компьютера.