Scala

Futureをマスターする -おまけ-

More than 3 years have passed since last update.


シリーズ目次

はじめに

コンパニオンオブジェクト操作

インスタンス操作

おまけ

Futureにハマる


おまけ

Future{ ... }と書けば、必ず新しいスレッドで処理が行われるのかと思っていたがそうではなく、実際は色々と複雑に制御されているようだ。

※Futureのソースコードを読んだわけではないので何故このような動きになるのかといった解説まではできていない。

object Main extends App {

println("in main : " + Thread.currentThread().getId())

val f1 = Future{ println(s"f1 : ${Thread.currentThread().getId()}"); 1 }
val f2 = Future{ println(s"f2 : ${Thread.currentThread().getId()}"); 2 }

val f = f1.flatMap { n1 =>
println(s"f1#flatMap : ${Thread.currentThread().getId()}");

f2.map{ n2 =>
println(s"f2#map : ${Thread.currentThread().getId()}");
n1 + n2
}
}

f.onSuccess {
case result => println(s"f#onSuccess : ${Thread.currentThread().getId()}")
}

Await.ready(f, Duration.Inf)
}

この実行結果は以下となる。

f1とf2は同一スレッドで実行されていることが確認できる。冒頭で述べたFuture{...}と書けば別スレッドで処理されると思っていたら違っていた、はこれを見たことによる。

main : 1296

f1 : 1290
f2 : 1290
f1#flatMap : 1297
f2#map : 1297
f#onSuccess : 1297

次に、f1とf2を以下のように変える。

  val f1 = Future{ Thread.sleep(3000); println(s"f1 : ${Thread.currentThread().getId()}"); 1 }

val f2 = Future{ Thread.sleep(3000); println(s"f2 : ${Thread.currentThread().getId()}"); 2 }

結果は以下。

今度は別スレッドで実行されるようになった。

main : 1304

f1 : 1290
f1#flatMap : 1290
f2 : 1305
f2#map : 1290
f#onSuccess : 1290

次は更にonSuccessをforeachで書き換える。

  f.foreach {

println(s"f#foreach : ${Thread.currentThread().getId()}")
println(_)
}

結果は以下。

なんとforeachのスレッド番号はMainと同じになった。それを裏付けるように出力順序も早くに出てくる。

もちろん結果は一番最後。

プログラマから見るとonSuccessと同じかもしれないが、実際は細かいレベルで振る舞いは異なるようだ。

main : 1313

f#foreach : 1313
f1 : 1306
f2 : 1314
f1#flatMap : 1306
f2#map : 1306
3

ほんの少しの変更だが今度は以下のように変えてみた。

  f.foreach { r =>

println(s"f#foreach : ${Thread.currentThread().getId()}")
println(r)
}

結果は以下。

今度はforeachはMainとは別スレッドで呼ばれており、表示順も最後の方になった。

結果を変数に束縛するようにしたからなんだろう。

main : 1322

f1 : 1314
f1#flatMap : 1314
f2 : 1323
f2#map : 1314
f#foreach : 1314
3