Java 5.0時代の非同期処理技術から学び直すScala/Java非同期処理

Java 5.0時代の非同期処理技術から学び直すScala/Java非同期処理

14bfbc98a7d5ed3574be08f6b176ce70?s=128

リチャード 伊真岡

October 26, 2019
Tweet

Transcript

  1. Java 5.0時代の非同期処理技術から学び直す Scala/Java 非同期処理 マーベリック株式会社 リチャード 伊真岡

  2. 現代のアプリケーション開発は 非同期処理が重要

  3. モバイルアプリケーションでの 同時多発的な処理

  4. 処理がまずいと...

  5. サーバー側でも様々な処理を 同時に行う

  6. ZIO CatsEffect Monix Future Akka チュートリアルを見れば それっぽい物は書ける... Finagle

  7. 今日のながれ • 抽象度の低いものから高いものへと少しずつ学ぶ • Java5.0からの歴史を学ぶ、各技術の背景とつながりを学ぶ

  8. マルチスレッド Multi-threaded Concurrent Asynchronous 平行 非同期 用語について

  9. マルチスレッド Multi-threaded Concurrent Asynchronous 平行 用語について このスライドでは「非同期」という単語をよく使うが 平行・マルチスレッド処理でもある物が中心 非同期

  10. None
  11. Java Memory Model と Java 5 の非同期処理

  12. None
  13. None
  14. Oracle Concurrency guide https://docs.oracle.com/javase/tutorial/essentia l/concurrency/ 今日一番覚えておいて ほしい資料!!!

  15. Thread & Runnable public class MyRunnable implements Runnable { public

    void run() { ... } } Thread thread = new Thread(new MyRunnable()); thread.start();
  16. None
  17. java.util.concurrent (Java 5.0 以降)

  18. ExecutorService ExecutorService service = …; service.execute(runnable);

  19. None
  20. None
  21. None
  22. 非同期で起こる問題の一例

  23. thread interference (非atomic処理) class Counter { private int c =

    0; public void increment() { c++; } public void decrement() { c--; } public int value() { return c; } }
  24. None
  25. None
  26. 1. Thread A: int c 取得 2. Thread B: int

    c 取得 3. Thread A: 取得した値をIncrement 4. Thread B: 取得した値をDecrement 5. Thread A: cに1を書き込み 6. Thread B: cに-1を書き込み (6が5を上書き)
  27. synchronized class Counter { private int c = 0; public

    synchronized void increment() { c++; } public synchronized void decrement() { c--; } public int value() { return c; } }
  28. None
  29. None
  30. None
  31. synchronized atomic happens-before

  32. happens-before

  33. None
  34. None
  35. None
  36. data race (memory-consistency error)

  37. 順番(happens-before)があれば 期待される状態を導ける

  38. None
  39. 少なくともどちらか一方が writeである場合 conflicting access data race (問題) の原因

  40. Java Memory Model Java 5.0 (1.5) からの導入 https://www.cs.umd.edu/~pugh/java/memoryModel /jsr133.pdf http://gee.cs.oswego.edu/dl/jmm/cookbook.html

    http://www.cs.umd.edu/~pugh/java/memoryModel/
  41. 非同期処理の理論的な研究 Java Memory Modelは当時の学術 的な非同期処理研究にもとづい て作られている。

  42. Java 5.0ではSynchronizedの他にも data raceを避けるツールが導入された

  43. java.util.concurrent.locks Lock ReadWriteLock Condition ReentrantLock ...

  44. volatile “write to a volatile variable establishes a happens-before relationship

    with subsequent reads of that same variable” volatile int counter = 0;
  45. atomic java.util.concurrent.atomic AtomicInteger AtomicBoolean AtomicLong AtomicReference ... //CAS operation boolean

    compareAndSet(expectedValue, updateValue); CASはCPUでサポートされた操作 java.util.concurrent.atomicはそれを利用する
  46.  1.CAS成功例

  47.  2.CAS成功例

  48.  3.CAS成功例 memory location value == expected

  49.  1.CAS失敗例

  50.  2.CAS失敗例

  51.  3.CAS失敗例 memory location value != expected

  52. thread-safe collections java.util.concurrent ConcurrentHashMap ConcurrentLinkedQueue ... CASを利用

  53. • Java Memory Model 仕様 ◦ happens-before ◦ volatile, synchonized,

    locks • java.util.concurrency ツール ◦ locks, atomic, ExecutorService, thread-safe collections ◦ これらをうまく使うパターン、抽象度の更に高いツールは後年 Java 1.4 -> 5.0 時代のまとめ
  54. ここまでの話は後でくり返し出てきます!

  55. OSとCPU

  56. Netflixのパフォーマンス分析エンジニア Brendan Gregg氏が2013年に書いた CPUやメモリの動作がOSレベルでどう使わ れているかについて参考になる章がある http://www.brendangregg.com/sysperfbook.html

  57. None
  58. CPUとキャッシュ Stack Exchange - Where exactly L1, L2 and L3

    Caches located in computer? https://superuser.com/questions/1961 43/where-exactly-l1-l2-and-l3-caches-loc ated-in-computer キャッシュは「スレッド ごとのメモリ空間」に関 わる -> data race -> happens-before
  59. CPUとキャッシュ Stack Exchange - Where exactly L1, L2 and L3

    Caches located in computer? https://superuser.com/questions/1961 43/where-exactly-l1-l2-and-l3-caches-loc ated-in-computer キャッシュは「スレッド ごとのメモリ空間」に関 わる -> data race -> happens-before data race (memory-consistency error)
  60. None
  61. None
  62. None
  63. 同時に走るスレッドは 高々CPUコア数

  64. 同時に走るスレッドは 高々CPUコア数

  65. 同時に走るスレッドは 高々CPUコア数

  66. 同時に走るスレッドは 高々CPUコア数

  67. GUIとThread

  68. A Swing programmer deals with the following kinds of threads:

    • Initial threads, the threads that execute initial application code. • The event dispatch thread, where all event-handling code is executed. Most code that interacts with the Swing framework must also execute on this thread. • Worker threads, also known as background threads, where time-consuming background tasks are executed. スレッド使用例 Swing guide UI framework (Java 5.0以前から使われていた) https://docs.oracle.com/javase/tutorial/uiswing /concurrency/index.html
  69. None
  70. None
  71. None
  72. None
  73. BrowserのEvent Loop

  74. None
  75. event loop

  76. event loop

  77. main() function funC(c) { … //make an ajax call }

    function funcB(b) { return funcC(b); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  78. funcA() main() function funC(c) { … //make an ajax call

    } function funcB(b) { return funcC(b); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  79. funcB() funcA() main() function funC(c) { … //make an ajax

    call } function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  80. funcB() funcA() main() function funC(c) { … //make an ajax

    call } function funcB(b) { return funcC(b); } function funcA(a) { return funcB(a); } funcA(“my user data string”); funcC() call stack event loop
  81. funcB() funcA() main() function funC(c) { … //make an ajax

    call } function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); funcC() call stack event loop ajax コール
  82. funcB() funcA() main() function funC(c) { … //make an ajax

    call } function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  83. funcB() funcA() main() function funC(c) { … //make an ajax

    call } function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  84. funcA() main() function funC(c) { … //make an ajax call

    } function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  85. main() function funC(c) { … //make an ajax call }

    function funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); call stack event loop
  86. function funC(c) { … //make an ajax call } function

    funcB(b) { return funcC(c); } function funcA(a) { return funcB(a); } funcA(“my user data string”); event loop ajax コールバック タスクキュー
  87. タスクキューも含めたアニメーション

  88. ajax コール

  89. ajax コールバック

  90. None
  91. None
  92. None
  93. 関数型プログラミングと immutable object

  94. 少なくともどちらか一方が writeである場合 conflicting accessは data race (問題) の原因

  95. Java Memory Model でもdata raceは主要な関心事 https://www.cs.umd.edu/~pugh/java/memoryMod el/jsr133.pdf http://gee.cs.oswego.edu/dl/jmm/cookbook.html http://www.cs.umd.edu/~pugh/java/memoryModel/

  96. None
  97. None
  98. オブジェクトを変更する際は新しいインスタンス を作成、元のオブジェクトはそのまま

  99. Oracle Concurrency guide もimmutable objectに 言及している • setterダメ • final,

    private • ...
  100. None
  101. Akkaとactor model

  102. rich, realtime -> 状態 ?

  103. IDを持つオブジェクトの内部状態が変化する例

  104. IDでクエリ

  105. IDでクエリ

  106. IDを持つオブジェクトのインスタンスが 複数スレッドに存在する場合

  107. どのスレッドに対して クエリ?

  108. デーモンスレッドにオブジェクトのインスタンスを保持

  109. デーモンスレッドにオブジェクトのインスタンスを保持

  110. デーモンスレッドにオブジェクトのインスタンスを保持

  111. None
  112. クエリはできたしかし...

  113. 少なくともどちらか一方が writeである場合 conflicting accessは data race (問題) の原因

  114. conflicting accessはの正しい管理は難しい

  115. conflicting accessはの正しい管理は難しい

  116. shared mutable: 複雑さの元凶 shared mutable: おもしろさの源

  117. アプリケーションの状態をすべて データベースで管理する方法

  118. データベース周りの ソースコードが複雑

  119. 複雑さのバランスを アプリケーション側 との間でとる

  120. オブジェクトの内部状態

  121. Actorでオブジェクトの内部状態を隠蔽

  122. Actorどうしはメッセージを送り合って通信

  123. Akka actorコードサンプル actor ! message class MyActor extends Actor {

    var state = ... def receive = { case MessageTypeX => ... case MessageTypeY => ... case MessageTypeZ => ... } }
  124. SenderはReceiverのスレッドセーフなキューに メッセージを送る

  125. None
  126. 複数Senderから同時に メッセージを送ることが可能

  127. Akka actorのメッセージキューは デフォルトで ConcurrentLinkedQueue

  128. Akka actorのメッセージキューは デフォルトで ConcurrentLinkedQueue

  129. Akka actorのメッセージキューは デフォルトで ConcurrentLinkedQueue

  130. Receiver側の動作を追っていく

  131. None
  132. 単一のスレッドがReceiverの receiveメソッドを走らせる スレッド1 スレッド2

  133. スレッド1 スレッド2 Receiverはキューから一つずつ メッセージを取り出し処理する

  134. スレッド1 スレッド2 Receiverの実行スレッドは 入れ替わることがある

  135. ActorのReceiveメソッドは いかなる時も最大一つのスレッド によって実行 スレッド0 スレッド1 スレッド2

  136. スレッド0 スレッド1 Receiverはキューからメッセージ がなくなったら次を待つ

  137. Akka official documentation https://doc.akka.io/docs/akka/current/general/jmm. html#actors-and-the-java-memory-model 自分でvolatileを使う 必要はない Akkaが面倒を見てくれる ActorのReceiveメソッドは いかなる時も最大一つのスレッド

    によって実行
  138. None
  139. volatile “write to a volatile variable establishes a happens-before relationship

    with subsequent reads of that same variable”
  140. > さらにvolatileな変数をreadす るとき、read側のスレッドは最 新のvolatile変数の変更のみなら ず、そこへ至るまでのコードの 副作用まで観測することになる Oracle concurrency guide

  141. akkaのDispatcherは内部で ExecutorServiceへの参照を持つ akkaの ! メソッドはDispatcher のdispatchメソッドの内部で ExecutorServiceのexcecuteメ ソッドを呼ぶ するとThread Poolに対して

    Actorのreceiveメソッドをスケ ジュールする Dispatcher Thread Pool ExecutorService dispatch() execute()
  142. Akka actor modelまとめ • shared mutableは難しいが重要な場合がある • actor modelはshared mutable管理の一つの解

    • AkkaはJava 5.0時代からのツールを内部で使う
  143. 関数型方面の近年の進化

  144. Scalaで非同期処理を主要機能に含むライブラリ Scala標準 Monix Cats Effect ZIO

  145. 共通なパターン

  146. Cats Effect SemaphoreやMVarなどの 低レベルな非同期処理の パーツを acquire, releaseなどの コンビネータとともに定義 https://typelevel.org/cats-effect/

  147. Cats Effect 安全に非同期処理の低レベ ルなbuilding blockを実装

  148. Cats Effect Scalaのfor文とモナドを組 み合わせるテクニックに よってChainingを実現 型安全なので各処理ステッ プの戻り値型とエラー型が 合わなければコンパイラが エラーで教えてくれる https://typelevel.org/cats-effect/

  149. https://typelevel.org/cats-effect/

  150. https://typelevel.org/cats-effect/ Swing GUI Event Dispatcher Background Threads Main Thread

  151. https://typelevel.org/cats-effect/ https://typelevel.org/cats-effect/ Event Loop Event Loop Service Worker Web API/File

    IO
  152. https://typelevel.org/cats-effect/ https://typelevel.org/cats-effect/ Akka Default Dispatcher Future on thread pool IO

    thread pool
  153. 今話題のEnvoyでもEvent Loopモデルは取り入れられ ている ここまでの話を踏まえれば 比較的容易に理解できるは ず https://blog.envoyproxy.io/envoy-threading- model-a8d44b922310

  154. ZIO try/catchブロックを型安全に 行う例 ファイルやデータベースのリ ソース開放に便利 https://zio.dev/

  155. ZIO こちらもコンビネータを使って Chainingを行うことができる https://zio.dev/

  156. None
  157. None
  158. ZIO fiberもAkka Actorsも 軽量スレッドとしての役割を果 たす。 OSスレッドはメモリ使用量も多 くかつスイッチングコストも高 いので、軽量スレッドのほうが 効率が良い OS

    Threads Actors OS Threads Fibers Akka ZIO
  159. 自分の過去 • 新卒から9年間自社言語: シングルスレッド・同期IOのみ • Javaの非同期怖かった • actor modelの実装であるAkkaに興味を持つ

  160. 非同期処理を楽しく学ぶ • わかりにくかったら抽象度の低いものを先に学ぶのもアリ • 今回の発表からひとつだけでも興味を持って欲しい

  161. マーベリックではエンジニア募集中

  162. 文献 https://twitter.com/csgillespie/status/… CPUクロック周波数に関するグラフ付きツイート。 Computer Hope processor history https://www.computerhope.com/history/processor.htm AMDが2005年に初のdual-coreを発売など。

  163. Oracle Concurrency guide https://docs.oracle.com/javase/tutorial/… Java 5.0やそれ以前から続く非同期処理技術に詳しい。 Java Memory ModelとJava 5.0

    Oracle Concurrency in Swing guide https://docs.oracle.com/javase/tutorial/uiswing/... UIでのスレッド管理プラクティスとして一部今も通用する。
  164. The JSR-133 Cookbook for Compiler Writers  http://gee.cs.oswego.edu/dl/jmm/cookbook.html JSR-133に深く関わったDoug Lea教授による情報。 The

    Java Memory Model  http://www.cs.umd.edu/~pugh/java/memoryModel/ JSR-133仕様書に載っていない周辺情報。 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms https://www.cs.rochester.. ConcurrentLinkedQueue実装に使われたアルゴリズム。 JSR-133 https://www.cs.umd.edu/~pugh/… Java Memory Model仕様書。happens-beforeを始め様々な概 念やコンパイラ最適化の影響などが解説されている。
  165. OSとCPU Systems Performance https://www.amazon.com/gp… Netflixのパフォーマンス分析エンジニアBrendan Gregg氏に よる著書。CPUとスレッドについての章がある。 Brendan Gregg’s Home

    page http://www.brendangregg.com/overview.html Brendan Gregg氏の著書にも関わる様々な情報がある See How a CPU works https://youtu.be/cNN_tTXABUA Scott CPUという教育用の仮想的なCPUを題材に動画で解説 している。
  166. GUIとThread The Node.js Event Loop, Timers, and process.nextTick() https://nodejs.org/uk/docs/guides/... 公式ドキュメント。各フェーズ毎の詳細な解説。

    GitHub: libuv https://github.com/libuv/libuv/... node.js内部で使われているイベントループの実装を含む非同 期I/Oライブラリ。Cで書かれている。 YouTube: What the heck is the event loop anyway? JSConf https://youtu.be/8aGhZQkoFbQ アニメーションを駆使したevent loop解説。100万再生以上。
  167. MDN web docs: Concurrency model and Event Loop https://developer.mozilla.org/en-US/docs/... MozillaによるEvent

    Loop解説 Akka docs: https://akka.io/docs/ 公式のドキュメント edX: Programming Reactive Systems https://www.edx.org/... AkkaのメンテナであったKonrad Malawski氏が中心となって 作ったオンライン学習コース Akkaとactor model
  168. 関数型方面の進化(Scala) Monix https://monix.io/ イベント駆動で高パフォーマンスな非同期プログラムを書くサ ポートをするライブラリ。 Cats Effect https://typelevel.org/cats-effect/ I/Oを表す型を提供するライブラリ。同期および非同期の Effectを表現することができる。

    ZIO https://zio.dev/ 型安全でcomposableで非同期な処理をかけるライブラリ。
  169. YouTube: The Making of an IO - Daniel Spiewak https://youtu.be/g_jP47HFpWA

    関数型プログラムと非同期処理の関係を説明。Cats Effectの思想 やスレッドプール使い分けベストプラクティスにも触れる