Для первых двух примеров ваши ожидания кажутся правильными.В третьем примере кажется разумным ожидать, что t завершит работу до того, как основной поток начнет ждать, а затем основной поток зависнет до истечения времени ожидания.
Но, как вы заметили, это не то, что происходит.
Ожидающий поток не прекращает ждать, пока его не прервут или не уведомят (за исключением ложных пробуждений, но это результат непредсказуемых состояний гонки; поведение в опубликованном коде происходит надежно, поэтому я думаю, что ложные пробуждения могутбыть исключенным здесь).
Так как ничто не мешает основному потоку, и его ожидание прервано, и мы исключили ложные пробуждения, он должен получать уведомление.Есть только одна вещь, которая может предоставить уведомление, и это поток t.
Чтобы t уведомил основной поток, он должен был быть живым в то время, когда t начал ждать.Так что же его держит?
Существует не очень известное поведение, которое возникает, когда поток завершается.Документация API для Thread.join гласит:
В этой реализации используется цикл вызовов this.wait, обусловленный this.isAlive.Когда поток завершает работу, вызывается метод this.notifyAll.Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll для экземпляров Thread.
Что происходит:
1) t печатает свой вывод и находится на выходе из своегометод run.
2) Между t и основным потоком существует гонка для захвата блокировки t.Это нужно для вызова notifyAll, главное нужно, чтобы войти в синхронизированный блок.Основной поток сначала захватывает блокировку.
3) t зависает до тех пор, пока не сможет захватить блокировку.
4) Основной поток входит в метод ожидания (снятие блокировки).
5) t получает блокировку и вызывает t.notifyAll.
5) Главный поток уведомляется и покидает метод ожидания (повторная блокировка).
Некоторые уроки:
Не синхронизировать по потокам (это хороший пример того, почему в документации API сказано не делать этого, здесь вы непреднамеренно задержали умирание потока своевременно).
Если поток не ожидает, он не получает уведомления.Если поток начинает ждать после того, как уведомление уже получено, это уведомление теряется.
Не полагайтесь исключительно на уведомления (это делает ваш код уязвимым для условий гонки), вместо этого используйте уведомлениянаряду с некоторым условием, которое может установить другой поток.Ожидание вызова в цикле с условием проверки.Если вы видите исходный код Thread.join, это хороший пример, он выглядит примерно так:
while (isAlive()) {
wait(0);
}
Не спите, удерживая блокировку.Это делает систему менее отзывчивой без какой-либо выгоды.
Будьте очень осторожны, делая предположения о порядке вещей.