1. 概要

このクイックチュートリアルでは、Kotlinの揮発性プロパティを使用して変数のメモリの可視性を制御する方法を説明します。

まず、Kotlinの揮発性プロパティを紹介します。 次に、さらに深く掘り下げて、バイトコードレベルでこれらのプロパティの内部表現を確認します。

2. 揮発性の特性

Javaのバックグラウンドから来ていると、KotlinのJavaのvolatileキーワードに相当するものは何でしょうか。

問題をより具体的にするために、この例を見ていきましょう。

object TaskExecutor : Runnable {

    private var shouldContinue = true

    override fun run() {
        while (shouldContinue) {}
        println("Done")
    }

    fun stop() {
        shouldContinue = false
    }
}

TaskExecutor シングルトンオブジェクトは、shouldContinue変数がtrueに設定されている限り、スピン待機を続行します。

fun main() {
    val thread = Thread(TaskExecutor)
    thread.start()

    TaskExecutor.stop()

    thread.join()
}

stop()メソッドを呼び出すと、ビジー待機がすぐに終了し、TaskExecutor「Done」をコンソールに出力すると予想される場合があります。

ただし、共有マルチプロセッサアーキテクチャとCPUキャッシュの性質上、ビジーウェイトは遅延で終了する場合があります。 怪我に侮辱を加えると、それはまったく終わらないかもしれません

shouldContinue 変数でこの種のプロセッサまたはランタイムの最適化を回避するには、そのプロパティに@Volatileアノテーションを付ける必要があります。

object TaskExecutor : Runnable {

    @Volatile
    private var shouldContinue = true

    // same as before
}

@Volatile アノテーションは、アノテーション付きプロパティのJVMバッキングフィールドをvolatileとしてマークします。 したがって、このフィールドへの書き込みはすぐに他のスレッドに表示されます。 さらに、読み取りには常に最新の変更が表示されます。

簡単に言うと、 @Volatile アノテーションは、KotlinのJavaのvolatileキーワードに相当します。

3. JVM表現

揮発性プロパティのKotlinAPIについて十分に理解できたので、バイトコードを調べてみましょう。

TaskExecutorシングルトンオブジェクトをkotlincでコンパイルすると、次のようになります。

$ kotlinc TaskExecutor.kt

次に、結果のバイトコードをjavapで検査します。

$ javap -v -p -c TaskExecutor
// omitted for brevity
private static volatile boolean shouldContinue;
    descriptor: Z
    flags: (0x004a) ACC_PRIVATE, ACC_STATIC, ACC_VOLATILE

次に、shouldContinueフィールドがACC_VOLATILEフラグを使用して静的な値としてコンパイルされていることがわかります。これは、揮発性のプロパティであることを意味します。

4. 結論

この短いチュートリアルでは、揮発性プロパティを使用して変数のメモリの可視性を制御する方法を説明しました。 さらに、@Volatileアノテーション用に生成されたバイトコードをざっと見てみました。

揮発性プロパティが変数のメモリ可視性にどのように影響するかについての詳細は、Javaの揮発性キーワードガイドを確認することを強くお勧めします。

いつものように、すべての例はGitHubから入手できます。