Thread.join not behaving as I expected in scala Thread.join not behaving as I expected in scala multithreading multithreading

Thread.join not behaving as I expected in scala


It works if you transform the Range into a List:

  def ttest() = {     val threads =       for (i <- 1 to 5 toList)        yield new Thread() {          override def run() {            println("going to sleep")            Thread.sleep(1000)            println("awake now")          }        }    threads.foreach(t => t.start())    threads.foreach(t => t.join())    println("all done")  }

The problem is that "1 to 5" is a Range, and ranges are not "strict", so to speak. In good English, when you call the method map on a Range, it does not compute each value right then. Instead, it produces an object -- a RandomAccessSeq.Projection on Scala 2.7 -- which has a reference to the function passed to map and another to the original range. Thus, when you use an element of the resulting range, the function you passed to map is applied to the corresponding element of the original range. And this will happen each and every time you access any element of the resulting range.

This means that each time you refer to an element of t, you are calling new Thread() { ... } anew. Since you do it twice, and the range has 5 elements, you are creating 10 threads. You start on the first 5, and join on the second 5.

If this is confusing, look at the example below:

scala> object test {     | val t = for (i <- 1 to 5) yield { println("Called again! "+i); i }     | }defined module testscala> test.tCalled again! 1Called again! 2Called again! 3Called again! 4Called again! 5res4: scala.collection.generic.VectorView[Int,Vector[_]] = RangeM(1, 2, 3, 4, 5)scala> test.tCalled again! 1Called again! 2Called again! 3Called again! 4Called again! 5res5: scala.collection.generic.VectorView[Int,Vector[_]] = RangeM(1, 2, 3, 4, 5)

Each time I print t (by having Scala REPL print res4 and res5), the yielded expression gets evaluated again. It happens for individual elements too:

scala> test.t(1)Called again! 2res6: Int = 2scala> test.t(1)Called again! 2res7: Int = 2

EDIT

As of Scala 2.8, Range will be strict, so the code in the question will work as originally expected.


In your code, threads is deferred - each time you iterate it, the for generator expression is run anew. Thus, you actually create 10 threads there - the first foreach creates 5 and starts them, the second foreach creates 5 more (which are not started) and joins them - since they aren't running, join returns immediately. You should use toList on the result of for to make a stable snapshot.