Как синхронизированная работа в 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

В Java каждый Object предоставляет возможность для Thread to synchronize или блокировки на нем. Когда метод синхронизирован, метод использует экземпляр объекта как блокировку. В вашем примере методы bow и bowBack являются synchronized и оба находятся в одном классе Friend. Это означает, что любой поток, выполняющий эти методы, будет синхронизироваться с экземпляром Friend в качестве его блокировки.

Последовательность событий, которые вызывают тупик:

  • В первой теме были запущены вызовы alphonse.bow(gaston), которые synchronized в объекте alphonse Friend. Это означает, что Thread должен получить блокировку от этого объекта.
  • Во второй теме были запущены вызовы gaston.bow(alphonse), которые synchronized в объекте gaston Friend. Это означает, что Thread должен получить блокировку от этого объекта.
  • Первый запущенный поток теперь вызывает bowBack и ожидает, что блокировка на gaston будет выпущена.
  • Теперь второй поток вызывает bowBack и ждет, когда блокировка на alphonse будет выпущена.

Чтобы показать последовательность событий более подробно:

  • main() начинает выполняться в основном Therad (называть его Thread # 1), создавая два экземпляра Friend. Пока что так хорошо.
  • Основная тема запускает свой первый новый поток (назовите его Thread # 2) кодом new Thread(new Runnable() { .... Тема № 2 вызывает alphonse.bow(gaston), которая synchronized на объекте alphonse Friend. Таким образом, поток # 2 получает "блокировку" для объекта alphonse и вводит метод bow.
  • Здесь присутствует фрагмент времени, и исходный поток получает возможность выполнить больше обработки.
  • Основная тема запускает вторую новую тему (назовите ее "Тема № 3" ), как и первая. Тема № 3 вызывает gaston.bow(alphonse), которая синхронизируется с объектом gaston Friend. Поскольку никто еще не приобрел "блокировку" для экземпляра объекта gaston, Thread # 3 успешно получает эту блокировку и вводит метод bow.
  • Здесь присутствует временной срез, и в Thread # 2 появляется возможность сделать больше обработки.
  • Тема № 2 теперь вызывает bower.bowBack(this); с bower, являющейся ссылкой на экземпляр для gaston. Это логический эквивалент вызова gaston.bowBack(alphonse). Таким образом, этот метод synchronized на экземпляре gaston. Блокировка для этого объекта уже была приобретена и удерживается другим потоком (Thread # 3). Таким образом, Thread # 2 должен дождаться освобождения блокировки на gaston. Поток помещается в состояние ожидания, позволяя Thread # 3 выполнять дальше.
  • Тема № 3 теперь вызывает bowBack, которая в этом случае логически совпадает с вызовом alphonse.bowBack(gaston). Для этого ему необходимо получить блокировку для экземпляра alphonse, но эта блокировка сохраняется в Thread # 2. Этот поток теперь находится в состоянии ожидания.

И теперь вы находитесь в положении, в котором ни один поток не может выполнить. Оба Thread # 2 и Thread # 3 ждут выхода блокировки. Но ни один замок не может быть выпущен без прогресса Thread. Но ни один поток не может добиться прогресса без блокировки.

Таким образом: Тупик!

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

Ответ 2

Синхронизировано имеет два эффекта:

  • Во-первых, невозможно, чтобы две вызовы синхронизированных методов на одном объекте чередовали. Когда один поток выполняет синхронизированный метод для объекта, все другие потоки, которые вызывают синхронизированные методы для одного и того же объекта (приостановить выполнение), до тех пор, пока первый поток не будет выполнен с объектом.
  • Во-вторых, когда синхронизированный метод завершается, он автоматически устанавливает связь между событиями и последующим вызовом синхронизированного метода для одного и того же объекта. Это гарантирует, что изменения состояния объекта будут видны для всех потоков.

Короче говоря, он блокирует любые вызовы синхронизированных методов на одном и том же объекте.

Ответ 3

Все синхронизированные функции для одного и того же объекта. Маркировка метода "synchronized" очень похожа на установку "синхронизированного (этого) {" блока вокруг всего содержимого метода ". Причина, по которой я не говорю" идентичный", заключается в том, что я не знаю, не компромисс, испускает ли тот же байт-код или нет, но AFAIK определенный эффект времени выполнения тот же.

Тупик - это классическая инверсия блокировки. Один поток блокирует букву. Затем (или одновременно в многоядерной системе) другая нить блокирует газон. Эта часть требует, чтобы планирование потоков так просто происходило с чередованием в правых точках.

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

Ответ 4

Синхронизированный метод такой же, как включение всех этих методов в

synchronized(this) {
  /// code here ...
}

блок.

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

В вашем случае тупик происходит, когда Alphonse начинает кланяться в потоке 1, тем самым вводя синхронизированный блок. Затем поток 1 выгружается системой, поэтому Thread 2 может начинаться, и у Gaston есть поклоны. Но Гастон не может поклониться, потому что он синхронизируется на Alphonse, а Thread 1 уже имеет этот замок. Таким образом, он ждет, пока Thread 1 покинет этот блок. Затем система заменит Thread 1, который попытается вернуть поклонник Alphonse. Кроме того, он не может этого сделать, потому что Thread 2 имеет синхронизированную блокировку на Gaston. Обе нити теперь застряли, ожидая, когда кто-нибудь закончит поклоняться, прежде чем сможет поклониться...