Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

リチャード 伊真岡

October 26, 2019
Tweet

More Decks by リチャード 伊真岡

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. View Slide

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

    View Slide

  12. View Slide

  13. View Slide

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

    View Slide

  15. Thread & Runnable
    public class MyRunnable implements Runnable {
    public void run() { ... }
    }
    Thread thread = new Thread(new MyRunnable());
    thread.start();

    View Slide

  16. View Slide

  17. java.util.concurrent
    (Java 5.0 以降)

    View Slide

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

    View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. 非同期で起こる問題の一例

    View Slide

  23. thread interference (非atomic処理)
    class Counter {
    private int c = 0;
    public void increment() {
    c++;
    }
    public void decrement() {
    c--;
    }
    public int value() {
    return c;
    }
    }

    View Slide

  24. View Slide

  25. View Slide

  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を上書き)

    View Slide

  27. synchronized
    class Counter {
    private int c = 0;
    public synchronized void increment() {
    c++;
    }
    public synchronized void decrement() {
    c--;
    }
    public int value() {
    return c;
    }
    }

    View Slide

  28. View Slide

  29. View Slide

  30. View Slide

  31. synchronized
    atomic
    happens-before

    View Slide

  32. happens-before

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. data race
    (memory-consistency error)

    View Slide

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

    View Slide

  38. View Slide

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

    View Slide

  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/

    View Slide

  41. 非同期処理の理論的な研究
    Java Memory Modelは当時の学術
    的な非同期処理研究にもとづい
    て作られている。

    View Slide

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

    View Slide

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

    View Slide

  44. volatile
    “write to a volatile variable establishes
    a happens-before relationship with
    subsequent reads of that same
    variable”
    volatile int counter = 0;

    View Slide

  45. atomic
    java.util.concurrent.atomic
    AtomicInteger
    AtomicBoolean
    AtomicLong
    AtomicReference
    ...
    //CAS operation
    boolean compareAndSet(expectedValue, updateValue);
    CASはCPUでサポートされた操作
    java.util.concurrent.atomicはそれを利用する

    View Slide

  46.  1.CAS成功例

    View Slide

  47.  2.CAS成功例

    View Slide

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

    View Slide

  49.  1.CAS失敗例

    View Slide

  50.  2.CAS失敗例

    View Slide

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

    View Slide

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

    View Slide

  53. ● Java Memory Model 仕様
    ○ happens-before
    ○ volatile, synchonized, locks
    ● java.util.concurrency ツール
    ○ locks, atomic, ExecutorService, thread-safe collections
    ○ これらをうまく使うパターン、抽象度の更に高いツールは後年
    Java 1.4 -> 5.0 時代のまとめ

    View Slide

  54. ここまでの話は後でくり返し出てきます!

    View Slide

  55. OSとCPU

    View Slide

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

    View Slide

  57. View Slide

  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

    View Slide

  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)

    View Slide

  60. View Slide

  61. View Slide

  62. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  67. GUIとThread

    View Slide

  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

    View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. View Slide

  73. BrowserのEvent Loop

    View Slide

  74. View Slide

  75. event loop

    View Slide

  76. event loop

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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 コール

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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 コールバック
    タスクキュー

    View Slide

  87. タスクキューも含めたアニメーション

    View Slide

  88. ajax コール

    View Slide

  89. ajax コールバック

    View Slide

  90. View Slide

  91. View Slide

  92. View Slide

  93. 関数型プログラミングと
    immutable object

    View Slide

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

    View Slide

  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/

    View Slide

  96. View Slide

  97. View Slide

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

    View Slide

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

    View Slide

  100. View Slide

  101. Akkaとactor model

    View Slide

  102. rich, realtime -> 状態 ?

    View Slide

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

    View Slide

  104. IDでクエリ

    View Slide

  105. IDでクエリ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  111. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  123. Akka actorコードサンプル
    actor ! message
    class MyActor extends Actor {
    var state = ...
    def receive = {
    case MessageTypeX => ...
    case MessageTypeY => ...
    case MessageTypeZ => ...
    }
    }

    View Slide

  124. SenderはReceiverのスレッドセーフなキューに
    メッセージを送る

    View Slide

  125. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  131. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  138. View Slide

  139. volatile
    “write to a volatile variable establishes
    a happens-before relationship with
    subsequent reads of that same
    variable”

    View Slide

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

    View Slide

  141. akkaのDispatcherは内部で
    ExecutorServiceへの参照を持つ
    akkaの ! メソッドはDispatcher
    のdispatchメソッドの内部で
    ExecutorServiceのexcecuteメ
    ソッドを呼ぶ
    するとThread Poolに対して
    Actorのreceiveメソッドをスケ
    ジュールする
    Dispatcher
    Thread Pool
    ExecutorService
    dispatch()
    execute()

    View Slide

  142. Akka actor modelまとめ
    ● shared mutableは難しいが重要な場合がある
    ● actor modelはshared mutable管理の一つの解
    ● AkkaはJava 5.0時代からのツールを内部で使う

    View Slide

  143. 関数型方面の近年の進化

    View Slide

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

    View Slide

  145. 共通なパターン

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  153. 今話題のEnvoyでもEvent
    Loopモデルは取り入れられ
    ている
    ここまでの話を踏まえれば
    比較的容易に理解できるは

    https://blog.envoyproxy.io/envoy-threading-
    model-a8d44b922310

    View Slide

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

    View Slide

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

    View Slide

  156. View Slide

  157. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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でのスレッド管理プラクティスとして一部今も通用する。

    View Slide

  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を始め様々な概
    念やコンパイラ最適化の影響などが解説されている。

    View Slide

  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を題材に動画で解説
    している。

    View Slide

  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万再生以上。

    View Slide

  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

    View Slide

  168. 関数型方面の進化(Scala)
    Monix https://monix.io/
    イベント駆動で高パフォーマンスな非同期プログラムを書くサ
    ポートをするライブラリ。
    Cats Effect https://typelevel.org/cats-effect/
    I/Oを表す型を提供するライブラリ。同期および非同期の
    Effectを表現することができる。
    ZIO https://zio.dev/
    型安全でcomposableで非同期な処理をかけるライブラリ。

    View Slide

  169. YouTube: The Making of an IO - Daniel Spiewak
    https://youtu.be/g_jP47HFpWA
    関数型プログラムと非同期処理の関係を説明。Cats Effectの思想
    やスレッドプール使い分けベストプラクティスにも触れる

    View Slide