Двоичный поиск используется везде . Возьмите любой отсортированный набор из любой библиотеки языка (Java.NET, C++ STL и так далее), и они все будут использовать (или иметь опцию использовать), двоичный поиск для нахождения значений. В то время как верный, что необходимо редко реализовывать его, все еще необходимо понять принципы позади него для использования в своих интересах его.
Следует отметить один важный момент: блокируются не методы , а экземпляры объектов .
Когда вы вызываете alphonse.bow (gaston)
, он пытается получить блокировку на alphonse
. Получив блокировку, он печатает сообщение и вызывает gaston.bowBack (alphonse)
. В этот момент он пытается получить блокировку на Gaston
. Как только блокировка установлена, он печатает сообщение, затем снимает блокировку и, наконец, снимает блокировку на alphonse
.
В тупиковой ситуации блокировки устанавливаются в таком порядке, что нет возможности для любой поток для продолжения.
alphonse
gaston
gaston
- не может, потому что поток 2 уже имеет это. alphonse
- не может, потому что он уже есть в потоке 1. Альфонс и Гастон - два разных объекта. Каждый объект имеет встроенный монитор (блокировку), связанный с ним.
Это может произойти следующим образом:
alphonse создан. Его объектный монитор - 1.
Гастон создан. Его объектный монитор - 2.
alphonse.bow (gaston); Альфонсу теперь принадлежит замок №1
gaston.bow (alphonse); Гастон теперь владеет замком №2
Альфонс поклоняется Гастону и ждет шлюза №2 Гастон выкрикивает поклон. Назад на Альфонса и ждет блокировки №1
Разумный? Использование синхронизированных ключевых слов блокирует, которые экземпляры отслеживают на протяжении всего метода. Пример можно переписать следующим образом:
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(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());
}
}
}
}
Блокировки удерживаются на объектах Java, а не на методах java. Таким образом, когда метод synchronized используется для метода, он блокирует объект «this». В случае статического метода он блокирует объект класса.
Вы можете явно указать объект монитора, используя synchronized (object) {}
Чтобы добавить к simonn, et al.,
Если вы хотите визуализировать это, скомпилируйте и запустите программу и сгенерируйте дамп потока работающей программы. Вы можете сделать это, набрав Ctrl - Break в консоли Windows или выполнив команду kill -QUIT [pid]
в системе * nix. Это предоставит вам список всех потоков
, запущенных в вашей системе, и где они либо выполняются, либо ожидают, а также мониторы, которые потоки либо блокируются, либо ожидают блокировки.
Если вы измените имена потоков в их конструкторах, вам будет легче найти их в полном дампе потока:
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}, "Alphonse").start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}, "Gaston").start();
synchronized в определении метода - это сокращение для синхронизации самого объекта (this). По сути, это означает, что bow () и bowBack () не могут быть вызваны для одного объекта Friend одновременно.
Теперь, если оба Friends попадают в bow (), ни один из них не сможет вызвать метод bowBack () друг друга.
Мне пришлось бы дважды проверить, но я думаю, что синхронизированный метод блокирует объект класса, поэтому он блокирует другие синхронизированные методы в том же классе.
Я думаю, что он блокируется. сам объект класса, поэтому он даже блокирует различные экземпляры.
Отредактируйте, чтобы добавить:
Взгляните на эту часть спецификации языка Java
Каждый метод лука захватывает собственный монитор объектов. Затем оба пытаются отозвать лук другого объекта и блокируют ожидание другого монитора.