Pro Yearly is on sale from $80 to $50! »

FutureとThreadPool

 FutureとThreadPool

2019-05-18 JJUG CCC
#ccc_m1b

Fda899b3499ba4d6b2dbc763b3923759?s=128

s_edward

May 18, 2019
Tweet

Transcript

  1. FutureとThreadPool Shigeki Shoji @takesection #jjug_ccc #ccc_m1b

  2. クラウド、GPGPU • マルチコアプロセッサの性能を効率よく使って 並⾏計算 • ⼤量のコアを持つGPUをGeneral-purposeに使っ た機械学習 • リアクティブシステム(リアクティブ宣⾔) #jjug_ccc

    #ccc_m1b
  3. ThreadPool #jjug_ccc #ccc_m1b

  4. Executors • newCachedThreadPool • newFixedThreadPool • newSingleThreadExecutor • newWorkStealingPool •

    newScheduledThreadPool • newSingleThreadScheduledExecutor #jjug_ccc #ccc_m1b
  5. ForkJoinPool • commonPool #jjug_ccc #ccc_m1b

  6. CachedThreadPool • 新しいタスクが送信されると、スレッドに直接 タスクを渡す(handoffs) • キャッシュされたスレッドがないときは新規スレッ ドを作成する • プールサイズの制限を超えて新規スレッドが作成で きないときは、例外(RejectedExecutionException)

    がスローされる • アイドル状態のスレッドをキャッシュする期間は、 KeepAliveTimeで設定(デフォルトは60秒) #jjug_ccc #ccc_m1b
  7. FixedThreadPoolと SingleThreadExecutor • 新しいタスクをキューに追加して、固定数のス レッドを再利⽤する • SingleThreadExecutorは固定数が1で変更不可 #jjug_ccc #ccc_m1b

  8. WorkStealingPool(ForkJoinPool) • ForkJoinTaskを実⾏するためのExecutorService • タスクをさらにfork、joinして複数の⼩さなタスク に分割できるForkJoinTaskを実⾏することができる • プール内のスレッドがプールに送信されたタスクを ⾒つけて実⾏しようとするため、プロセッサの性能 を効率よく利⽤できる

    #jjug_ccc #ccc_m1b
  9. ScheduledThreadPoolと SingleThreadScheduledExecutor • 指定された時間経過後や周期的にタスクの実⾏ をスケジュールする • SingleThreadScheduledExecutorはスレッド数の変更 不可 #jjug_ccc #ccc_m1b

  10. Future #jjug_ccc #ccc_m1b

  11. Future • ExecutorServiceのsubmit、invokeAllで取得 • getメソッドで結果を返すまでブロックするこ とができる #jjug_ccc #ccc_m1b

  12. Future<Integer> source = executor.submit(new Callable<Integer>() { … }); int res

    = source.get(); Future<Void> sink = executor.submit(new Callable<Void>() { … }); sink.get(); Futureの結果を後続のFutureで使いたい場合 source sink #jjug_ccc #ccc_m1b
  13. CompletableFutureと CompletionStage • これまでのFutureのように計算結果を取得した 値を後続の計算する場合にFuture.get()のような ブロックを発⽣させないように、Futureのまま supplier、 function、 consumerに計算結果を渡 していくことができる

    #jjug_ccc #ccc_m1b
  14. private CompletableFuture<Void> process(int n) throws InterruptedException { return CompletableFuture. supplyAsync(()

    -> { logger.info(String.format("supplier: %d %s", n, Thread.currentThread().toString())); return n; }, executor). thenApply(x -> { logger.info(String.format(”function: %d %s", x, Thread.currentThread().toString())); return x * 2; }). thenAccept(x -> logger.info(String.format(”consumer: %d %s", x, Thread.currentThread().toString())) ); } supplier function consumer #jjug_ccc #ccc_m1b
  15. バックプレッシャーとFlow • reactive-streams 仕様に対応したバックプレッ シャーにより Push ベースの通信によるリソー ス管理の問題を回避できるインターフェース #jjug_ccc #ccc_m1b

  16. デッドロック • CachedThreadPoolは、スレッドを作成できない ときに例外が発⽣する #jjug_ccc #ccc_m1b

  17. • ForkJoinPool、FixedThreadPool、 SingleThreadExecutorは、タスクはキューに⼊る。 • スレッドは、Thread.sleep() や、IO待ちあるいは、 Future.get()のように別のスレッドの計算結果を待つ と、実際は何も処理をしていないにもかかわらず、 他のタスクがそのスレッドを使うことができなくな る。

    • すべてのスレッドがアクティブな時に、スレッドが 同じスレッドプールから別のスレッドを要求して計 算結果を待つとデッドロックが発⽣する。 Task #jjug_ccc #ccc_m1b
  18. デッドロックの回避 • ノンブロッキングIOを使う • CompletableFuture(CompletionStage)を使い計算 結果をCompletionStageにとどまり続けるように する • 異なるスレッドプールのスレッドに移譲する •

    スレッドプールがForkJoinPoolの場合は、 ForkJoinPool.ManagedBlockerを実装したブロッ キングタスクを使⽤する • ⼗分な数のスレッドプールを算出して使⽤する #jjug_ccc #ccc_m1b
  19. ご清聴ありがとうござ いました #jjug_ccc #ccc_m1b