Перевод грамотного объяснения примера из javadocs со Stackoverflow
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() {
@Override
public void run() {
// System.out.println("Thread 1");
alphonse.bow(gaston);
// System.out.println("Th: gaston bowed to alphonse");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// System.out.println("Thread 2");
gaston.bow(alphonse);
// System.out.println("2.gaston waiting alph bowed");
}
}).start();
}
}
Здесь нужно понять две важные вещи:
1) Что делает каждая из одновременно выполняющихся нитей?
2) Какие локи используются?
Начнем с конца. Вы создали два объекта класса Friend: alphonse и gaston. У каждого из них есть свой лок. Так что локов два: альфонсов и гастонов. При входе в синхронизированный метод объекта, лок этого объекта запирается, и освобождается - отпирается - когда из метода выходят.
Теперь о нитях. Первая, назовем ее нить Alphonse (с большой буквы, чтобы отличить от объекта alphonse) делает следующее (A - обозначает Alphonse)
A: alphonse.bow(gaston) - получает лок alphonse
A: gaston.bowBack(alphonse) - получает лок gaston
A: возвращается из обоих методов, тем самым освобождая лок
В это самое время нить Gaston...
G: gaston.bow(alphonse) - получает лок gaston
G: alphonse.bowBack(gaston) - получает лок alphonse
G: возвращается из обоих методов, тем самым освобождая лок
Теперь сведем эти данные вместе и получим ответ. Нити могут переплетаться (т. е. их события совершаться) в разных порядках. Дедлок, к примеру, получится, если опрядок будет таким:
A: alphonse.bow(gaston) - получает лок alphonse
G: gaston.bow(alphonse) - получает лок gaston
G: пытается вызвать alphonse.bowBack(gaston), но блокируется, ожидая лока alphonse
A: пытается вызвать gaston.bowBack(alphonse), но блокируется, ожидая лока gaston
В этом случае обе нити заблокированы и каждая ожидает, что другая отдаст лок. Но ни одна это не сделает, потому что для этого нужно завершить свой метод, а он заблокирован другой нитью. Поэтому они застряли на месте - дедлок.
Впрочем, возможно и другое переплетение, в котором одна из нитей успеет завершиться до начала второй:
A: alphonse.bow(gaston) - получает лок alphonse
A: gaston.bowBack(alphonse) - получает лок gaston
A: возвращается из обоих методов, открывая оба лока
G: gaston.bow(alphonse) - получает лок gaston
G: alphonse.bowBack(gaston) - получает лок alphonse
G: возвращается из обоих методов, открывая оба лока
В этом случае дедлока нет. Например, добавлен какой-то метод, позволяющий другой нити успеть выполниться.
Когда результат зависит от порядка одновременно происходящих событий (запланированного порядка или скорости выполнения), это называется "race condition" - "состоянием гонки". Не все race condition потенциально производят дедлок, однако, по моему опыту, дедлоки происходят только в race condition.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ