Ответ 1
Это неотъемлемая проблема, которая может произойти в Scala при выпуске ссылки на объект singleton до завершения построения. Это происходит из-за другого потока, пытающегося получить доступ к объекту ParCollectionInInitializerTest
, прежде чем он будет полностью сконструирован.
Это не имеет никакого отношения к методу main
, скорее, это связано с инициализацией объекта, который содержит метод main
- попробуйте запустить это в REPL, набрав выражение ParCollectionInInitializerTest
, и вы получите те же результаты. Он также не имеет ничего общего с рабочими потоками fork-join, являющимися потоками демона.
Объекты Singleton инициализируются лениво. Каждый объект singleton может быть инициализирован только один раз. Это означает, что первый поток, который обращается к объекту (в вашем случае, к основному потоку), должен захватить блокировку объекта и затем инициализировать его. Каждый другой поток, который приходит впоследствии, должен ждать, пока основной поток инициализирует объект и, в конце концов, освободит блокировку. Это то, как объекты singleton реализованы в Scala.
В вашем случае поток рабочего потока параллельного потока пытается получить доступ к объекту singleton для вызова doSomething
, но не может этого сделать, пока основной поток не завершит инициализацию объекта, поэтому он ждет. С другой стороны, основной поток ожидает конструктора до тех пор, пока параллельная операция не завершится, что обусловлено завершением всех рабочих потоков - основной поток удерживает блокировку инициализации для singleton все время. Следовательно, происходит взаимоблокировка.
Вы можете вызвать это поведение с фьючерсами от 2.10 или с помощью простых потоков, как показано ниже:
def execute(body: =>Unit) {
val t = new Thread() {
override def run() {
body
}
}
t.start()
t.join()
}
object ParCollection {
def doSomething() { println("Doing something") }
execute {
doSomething()
}
}
Вставьте это в REPL, а затем напишите:
scala> ParCollection
и REPL зависает.