RoomのDatabase Instanceの初期化についてのメモ

概要

Roomのcodelabで疑問に思う箇所があったのでメモ

codelabで紹介されているコード

codelabではRoomDatabaseのインスタンス生成はsingletonパターンで実装され、以下のようなコードになっている。

@Database(entities = [Word::class], version = 1)
public abstract class WordRoomDatabase : RoomDatabase() {

   abstract fun wordDao(): WordDao

   companion object {
        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context): WordRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) return tempInstance
            synchronized(this) {
                val instance = Room.databaseBuilder(
                        context.applicationContext,
                        WordRoomDatabase::class.java, "Word_database").build()
                INSTANCE = instance
                return instance
            }
        }
   }
}

@VolatileアノテーションKotlinのドキュメントでは次のように解説されている。

Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.

したがって、codelabのコードでは変数INSTANCEにVolatileアノテーションが付けることで複数のスレッドからアクセスをスレッドセーフにしている。

疑問

synchronizedでlockを取得する前に他のスレッドがlockを解放した場合、codelabのコードでは既にインスタンスが生成済みでもRoom.databaseBuilderが呼ばれてしまうでは?

どうするか?

以下の記事の対策3に今回のコードは近くて、synchronizedブロック内で変数instanceが初期化されているかを確認している。

シングルトンパターンの遅延初期化をスレッドセーフにするには - じゅんいち☆かとうの技術日誌

試しにPlaidのコードを見ると、synchronizedブロックの中でinstanceを再度チェックしていた。

// For Singleton instantiation
@Volatile private var instance: DesignerNewsDatabase? = null

fun getInstance(context: Context): DesignerNewsDatabase {
    return instance ?: synchronized(this) {
        instance ?: buildDatabase(context).also { instance = it }
    }
}

まとめ

synchronizedブロックの中でinstanceが初期化済みかを再度チェックした方がよさそう。