Новый поток пытается вызвать run
в классе Lock
. Этот метод сам пытается создать новый экземпляр Lock
. Он не может сделать это 1 , пока класс Lock
не завершит инициализацию. JVM знает, что другой поток уже инициализирует класс, поэтому он блокируется, пока эта инициализация не будет завершена.
К сожалению, эта инициализация не может быть завершена до завершения run
из-за вызова t.join()
. Таким образом, каждый из двух потоков нуждается в другом, чтобы добиться прогресса, прежде чем он сможет что-либо сделать - тупик.
Определенно стоит попытаться избежать большой работы в инициализаторах классов, именно по этой причине.
Это могло бы произойти, даже если сам метод run()
был бы пустым. Но это еще хуже - потому что метод run()
не завершится, пока не создаст другой поток и не дождется, пока этот поток завершит вызов run()
и т. Д. Так что это еще одна причина сбоя - он будет в основном порождают потоки, пока у JVM не закончились ресурсы. Таким образом, даже удаление инициализатора типа не приведет вас к рабочему коду.
Что касается инициализации типа, см. раздел 12.4.1 JLS :
Класс T или интерфейс типа T будут инициализированы непосредственно перед первым возникновением любого из следующих действий:
- T является классом, и создается экземпляр T.
- Вызывается статический метод, объявленный T.
- Назначено статическое поле, объявленное T.
- Используется статическое поле, объявленное T, и поле не является константной переменной (§4.12.4).
Выражение new Lock()
всегда будет либо инициализировать Lock
, либо ждать завершения инициализации (что, конечно, уже могло произойти).
1 Если вы разделите код в run
, чтобы создать экземпляр Lock
, а затем войти в систему, а затем запустить поток, вы увидите, что это создание Lock
, это блокировки.