【Kotlin/Android】AtomicIntegerでスレッドセーフにカウンターを扱う方法

この記事からわかること
- Android Studio/KotlinのAtomicIntegerの使い方
- スレッドセーフなカウンターを実装するには?
- synchronizedの使い方
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
Kotlinで異なるスレッドからの操作の注意点
Kotlinで異なるスレッドから変更を加えるような実装にすると期待する通りに反映されないことがあります。例えば以下のように100個のスレッドからcounter
を参照してインクリメントさせる実装では結果が100000
になるはずですが、出力結果は少し欠落した値になってしまっています。
var counter = 0
val threads = List(100) {
thread {
repeat(1000) {
counter++
}
}
}
threads.forEach { it.join() }
println("Final counter value: $counter")
// Final counter value: 98298
この結果は予期せぬバグの温床になるので異なるスレッドから値を参照する際は適切に管理する必要があります。
AtomicInteger
AtomicInteger
はスレッドセーフな整数型です。先ほどの問題はこのAtomicInteger
を使用することで解決できました。
val counter = AtomicInteger(0)
val threads = List(100) {
thread {
repeat(1000) {
counter.incrementAndGet()
}
}
}
threads.forEach { it.join() }
println("Final counter value: ${counter.get()}")
// Final counter value: 100000
ここからはAtomicInteger
の使い方をまとめていきます。
インスタンス化
インスタンス化するにはAtomicInteger
のコンストラクタを使用します。
val counter = AtomicInteger(0)
値の取得
現在の値を取得するにはget
メソッドを使用します。
val count = counter.get()
新しい値を設定
新しい値を設定するにはset
メソッドを使用します。
counter.set(10)
インクリメントして取得
インクリメントして取得するにはincrementAndGet
メソッドを使用します。
val count = counter.incrementAndGet()
デクリメントして取得
デクリメントして取得するにはdecrementAndGet
メソッドを使用します。
val count = counter.decrementAndGet()
指定した値を加算して取得
指定した値を加算して取得するにはaddAndGet
メソッドを使用します。
val count = counter.addAndGet(4)
指定した値なら指定した値に更新して取得
指定した値なら指定した値に更新して取得するにはcompareAndSet(int expect, int update)
メソッドを使用します。
val wasUpdated = counter.compareAndSet(15, 20)
synchronizedで同期化
スレッドセーフにカウンターを扱う方法としてsynchronized
ブロックを使用することで期待通りに動作させることも可能です。
var counter = 0
val lock = Any()
val threads = List(100) {
thread {
repeat(1000) {
synchronized(lock) {
counter++
}
}
}
}
threads.forEach { it.join() }
println("Final counter value: $counter")
// Final counter value: 100000
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。