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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  17. 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 full-size slide

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

    View full-size slide

  19. synchronized
    atomic
    happens-before

    View full-size slide

  20. happens-before

    View full-size slide

  21. data race
    (memory-consistency error)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30.  1.CAS成功例

    View full-size slide

  31.  2.CAS成功例

    View full-size slide

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

    View full-size slide

  33.  1.CAS失敗例

    View full-size slide

  34.  2.CAS失敗例

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  40. 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 full-size slide

  41. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  46. GUIとThread

    View full-size slide

  47. 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 full-size slide

  48. BrowserのEvent Loop

    View full-size slide

  49. 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 full-size slide

  50. 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 full-size slide

  51. 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 full-size slide

  52. 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 full-size slide

  53. 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 full-size slide

  54. 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 full-size slide

  55. 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 full-size slide

  56. 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 full-size slide

  57. 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 full-size slide

  58. 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 full-size slide

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

    View full-size slide

  60. ajax コール

    View full-size slide

  61. ajax コールバック

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  64. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  67. Akkaとactor model

    View full-size slide

  68. rich, realtime -> 状態 ?

    View full-size slide

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

    View full-size slide

  70. IDでクエリ

    View full-size slide

  71. IDでクエリ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  107. 共通なパターン

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  123. 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 full-size slide

  124. 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 full-size slide

  125. 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 full-size slide

  126. 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 full-size slide

  127. 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 full-size slide

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

    View full-size slide

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

    View full-size slide