Как синхронизируется работа в Java - PullRequest
15 голосов
/ 15 апреля 2009

Сначала вот пример :

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

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

Что именно блокирует синхронизированный? Одна и та же функция работает для того же объекта (как я изначально думал)? Одна и та же функция для всех объектов одного и того же класса? Все синхронизированные функции для одного и того же объекта? Все синхронизированные функции для всех объектов одного класса?

Помоги мне здесь.

Ответы [ 4 ]

26 голосов
/ 15 апреля 2009

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

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

  1. Первый запущенный поток вызывает alphonse.bow(gaston), то есть synchronized для объекта alphonse Friend. Это означает, что Поток должен получить блокировку от этого объекта.
  2. Второй поток начал вызывать gaston.bow(alphonse), то есть synchronized для объекта gaston Friend. Это означает, что поток должен получить блокировку от этого объекта.
  3. Первый запущенный поток теперь вызывает bowback и ожидает снятия блокировки на gaston.
  4. Второй запущенный поток теперь вызывает bowback и ожидает снятия блокировки на alphonse.

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

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

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

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

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

2 голосов
/ 15 апреля 2009

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

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

блок.

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

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

2 голосов
/ 15 апреля 2009

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

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

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

2 голосов
/ 15 апреля 2009

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

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

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

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