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が初期化済みかを再度チェックした方がよさそう。