【Kotlin】コルーチンはJavaのスレッドとどう違うの?

Javaで便利なクラスの1つにスレッドクラスがあります。

Java でのスレッドはよくアンドロイド開発で使用していました。
ユーザの動きをスレッドで管理したりタイマー処理をスレッドで実装したりなど。
他には Tomcat を起動したらたくさんのスレッドが起動していたりしていますよね。

スレッドクラスは便利な反面デメリットがあります。
それはスレッドを多数使用するとパフォーマンスが極端に低下することです。

そこで Kotlin では Java のスレッドクラスの欠点を補うために、非同期処理を実行するための新しい仕組みを用意しました。それが今回紹介する「コルーチン」です。

Kotlin のコルーチンは kotlin.coroutines というパッケージで作成されていますがこれは低レベルなAPIのためこれを直接使うのにはとても不便です。

低レベルということはOSIモデルのネットワーク階層が低いことを意味していますのでフレームワークの中で実装されているような地道なソースコードということです。
普段呼んでいる便利なAPIは人間が分かりやすい高レベルなAPIとなります。

そこで kotlin.coroutines をベースとして高レベルなAPIを含むパッケージとして kotlinx.coroutines が用意されています。


Kotlin を開発するには統合開発環境である IntelliJ が便利です。
私の方で IntelliJ のインストール手順を記事にしましたので気になる方はこちらからどうぞ。

広告

動作環境

  • IntelliJ IDEA 2023.1.4 (Community Edition)
  • openjdk-20.0.2_windows-x64_bin

プロジェクトの新規作成

  • IntelliJ IDEAを起動します。

  • 「ファイル > 新規 > プロジェクト」を選択します。

  • 「Kotlin Multiplatform」を選択します。
    Project templateで「Console Application」を選択します。
    JDKを選択します。
    Nameで「プロジェクト名」(ここでは Coroutine)を入力します。

  • 「完了」を押下します。
  • プロジェクトが立ち上がります。

依存性の追加

  • build.gradle.ktsファイルの dependencies に下記の1行を追加します。
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
  • アイコンが表示されるので同期を実行します。

  • 同期が完了しました。

Main.ktの作成

下記のとおり Main.kt を作成します。

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

// runBlocking内はコルーチンスコープとなりコルーチン独自の処理を書くことができます
fun main(args: Array<String>) = runBlocking {
    // GlobalScope.async内は非同期処理になります
    val a = GlobalScope.async { doThread(10, 10, "A") }
    val b = GlobalScope.async { doThread(10, 20, "B") }

    // await()で非同期処理が終了するまで待ちます。
    print("<<< A=${a.await()} & B=${b.await()} >>>")
}

// funにsuspendを付けることでその関数はコルーチンスコープとなりコルーチン独自の処理を書くことができます
suspend fun doThread(count: Int, delayTime: Long, type: String): Long {
    val s = if (type == "A") "=" else "*"

    // measureTimeMillis内の処理時間を計測して返します
    val time = measureTimeMillis {
        for (n in 1..count) {
            println("[${type}] ${n} count " + s.repeat(5))
            // delayTimeミリ秒間、処理を中断します
            delay(delayTime)
        }
        // repeat関数は引数の数字の数だけ文字列を複製します
        println(s.repeat(3) + " ${type} finished " + s.repeat(3))
    }
    return time
}

メインプログラムの実行

  • 再生ボタンを押下して「'Main.kt'の実行」を押下します。
  • 実行できました。
[A] 1 count =====
[B] 1 count *****
[A] 2 count =====
[B] 2 count *****
[A] 3 count =====
[B] 3 count *****
[A] 4 count =====
[A] 5 count =====
[B] 4 count *****
[A] 6 count =====
[A] 7 count =====
[B] 5 count *****
[A] 8 count =====
[A] 9 count =====
[B] 6 count *****
[A] 10 count =====
=== A finished ===
[B] 7 count *****
[B] 8 count *****
[B] 9 count *****
[B] 10 count *****
*** B finished ***
<<< A=177 & B=319 >>>
プロセスは終了コード 0 で終了しました

参考書籍

こちらの書籍を参考にさせて頂きました。

おわりに

某サイトで Kotlin でのコルーチンを使った場合どういう利点があるか検索してみました。

  1. 簡潔で読みやすいコード: Kotlinコルーチンは、非同期処理をシンプルで直感的な方法で表現することができます。通常のコールバックベースの非同期コードよりも、より直感的で読みやすいコードを書くことができます。
  2. 非同期処理の合成: コルーチンは、複数の非同期タスクを組み合わせることが容易です。シンプルな言語機能を使用して、並行処理や逐次処理などの非同期操作を効率的に組み合わせることができます。
  3. スレッド管理の自動化: コルーチンは、スレッドの作成や管理を自動的に行ってくれます。これにより、開発者は明示的にスレッドを操作する必要がなくなり、煩雑なスレッドの同期やロックに対する心配を減らすことができます。
  4. 非同期例外処理: コルーチンは例外処理を簡単に扱うことができます。通常の非同期コードでは例外処理が複雑になりがちですが、コルーチンはエラーハンドリングをシンプルに行うことができます。
  5. スケーラビリティ: Kotlinコルーチンは、大規模なアプリケーションでも優れたスケーラビリティを提供します。非同期タスクの制御をより柔軟に行うことで、アプリケーションのパフォーマンスを向上させることができます。
  6. キャンセルサポート: コルーチンはタスクのキャンセルをサポートしています。非同期処理が不要になった場合や、タイムアウトが発生した場合などに、タスクのキャンセルを容易に実現できます。

Kotlin のコルーチンが Java のスレッドを凌駕するか今後に期待ですね。。

最後までお読み頂きありがとうございました。

広告

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


ABOUT US
ぽむ
はじめまして! ぽむ と申します。 Java、Kotlin (Android)、VBAなどの開発経験があるITエンジニアです。 備忘録として始めたブログですがみなさまのお役に立てたら光栄です。 英語など IT 以外の話題にも触れていこうと思っています。 詳しい自己紹介についてはこちら! よろしくお願いします。