Учитывая следующий код Java:
public class Test {
static private class MyThread extends Thread {
private boolean mustShutdown = false;
@Override
public synchronized void run() {
// loop and do nothing, just wait until we must shut down
while (!mustShutdown) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Exception on wait()");
}
}
}
public synchronized void shutdown() throws InterruptedException {
// set flag for termination, notify the thread and wait for it to die
mustShutdown = true;
notify();
join(); // lock still being held here, due to 'synchronized'
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
Thread.sleep(1000);
mt.shutdown();
} catch (InterruptedException e) {
System.out.println("Exception in main()");
}
}
}
Выполнение этого подождет одну секунду, а затем завершится правильно. Но для меня это неожиданно, я ожидаю, что здесь произойдет тупик.
Я рассуждаю следующим образом: вновь созданный MyThread будет выполнять run (), который объявлен как «синхронизированный», так что он может вызывать wait () и безопасно читать «mustShutdown»; во время этого вызова wait () блокировка снимается и повторно устанавливается при возврате, как описано в документации на wait (). Через одну секунду основной поток выполняет shutdown (), который снова синхронизируется, чтобы не обращаться к mustShutdown одновременно с его чтением другим потоком. Затем он пробуждает другой поток через notify () и ждет его завершения через join ().
Но, на мой взгляд, другой поток не может когда-либо вернуться из wait (), поскольку ему необходимо повторно получить блокировку объекта потока перед возвратом. Это невозможно, потому что shutdown () по-прежнему удерживает блокировку внутри join (). Почему он все еще работает и выходит правильно?