Kotlinで「Timer」を使った時間制御処理を学ぶ際には、仕組みの正体・スレッドモデル・例外時の挙動を正しく理解することが非常に重要です。
表面的な使い方だけを覚えると、実務では思わぬ不具合につながる可能性があります。
本稿では、KotlinにおけるTimerの本質から、正しい使い分け、注意点、代替手段までを体系的に解説します。
KotlinのTimerの正体
Kotlinには「Kotlin専用のTimerクラス」が存在するわけではありません。
JVM環境においてKotlinで利用されるTimerは、Java標準ライブラリの java.util.Timer および TimerTask です。
KotlinはJavaと完全互換であるため、JavaのTimerをそのまま利用できます。
Kotlin標準ライブラリによる拡張関数
Kotlinでは、kotlin.concurrent パッケージにより、Timerをラムダ式で簡潔に書ける拡張関数が提供されています。
単発実行の例
import java.util.Timer
import kotlin.concurrent.schedule
fun main() {
Timer().schedule(3000) {
println("3秒後に実行されました")
}
}
この schedule は、内部的には TimerTask を生成して登録しています。
戻り値は TimerTask であり、後から cancel() することも可能です。
定期実行と「間隔」の正確な意味
Timerには 2種類の定期実行方式 があり、ここは特に誤解されやすいポイントです。
schedule(固定ディレイ方式)
Timer().schedule(delay = 0, period = 1000) {
println("定期実行")
}
- 次の実行は 前回の処理が終了してから period ミリ秒後
- 処理が重くなると、実行タイミングはどんどん後ろにずれていく
scheduleAtFixedRate(固定レート方式)
Timer().scheduleAtFixedRate(delay = 0, period = 1000) {
println("定期実行")
}
- 次の実行は 前回の開始時刻から period ミリ秒後
- 遅れが出ると、間隔を詰めて実行しようとする
※ ただしTimerは単一スレッドのため、実際には完全な巻き戻し実行はできません。
Timerのスレッドモデル
Timerはシングルスレッド
Timer は 1つのTimerインスタンスにつき、1本のバックグラウンドスレッド を持ちます。
- 複数の
TimerTaskを登録しても同時実行されない - すべてのタスクは順番に処理される
重い処理による影響
Timer().schedule(0, 1000) {
Thread.sleep(3000)
println("実行")
}
この場合、1秒ごとに実行されることはなく、処理時間に引きずられて実行が遅延します。
例外処理の重大な落とし穴
Timerの最も危険な特性のひとつが、例外耐性の弱さです。
事実として重要な点
TimerTask内で 未捕捉の例外(RuntimeExceptionなど) が発生すると- Timerのスレッド自体が終了
- 以後、すべてのタスクが実行されなくなる
Timer().schedule(1000) {
throw RuntimeException("例外発生")
}
この仕様はTimer全体に影響するため、実務では致命的になり得ます。
TimerおよびTimerTaskの停止方法
Timer全体を停止する
val timer = Timer()
timer.cancel()
- 登録済みタスクはすべて破棄
- このTimerは再利用不可
特定のタスクだけ停止する
val task = Timer().schedule(0, 1000) {
println("実行")
}
task.cancel()
Kotlinの schedule は TimerTask を返すため、このような制御が可能です。
AndroidでTimerを使う際の注意点
UIスレッド操作は禁止
TimerTaskはバックグラウンドスレッドで実行されます。
Androidでは、UI操作を直接行うとクラッシュします。
// NG
textView.text = "更新"
正しい対応
runOnUiThread {
textView.text = "更新"
}
ただし、AndroidではTimer自体がライフサイクル管理と相性が悪いため、基本的には推奨されません。
Timerが実務で敬遠される理由
Timerはシンプルですが、設計上の弱点があります。
| 問題点 | 内容 |
|---|---|
| スレッド | シングルスレッド |
| 例外 | 1回の例外で全停止 |
| 精度 | 処理遅延に弱い |
| Android | Lifecycleと非親和 |
現代的な代替手段
Kotlin Coroutines
import kotlinx.coroutines.*
launch {
delay(3000)
println("3秒後に実行")
}
- 例外分離が可能
- キャンセル制御が明確
- Android / サーバー両対応
定期処理(Coroutine)
launch {
while (isActive) {
println("1秒ごと")
delay(1000)
}
}
ScheduledExecutorService(JVM標準)
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
val scheduler = Executors.newScheduledThreadPool(1)
scheduler.scheduleAtFixedRate({
println("定期実行")
}, 0, 1, TimeUnit.SECONDS)
Timerはいつ使うべきか?
使用しても問題ないケース
- 学習目的
- 非常に軽量な一時処理
- 短命なJVMツール
避けるべきケース
- Androidアプリ
- 長時間稼働するサーバー
- 定期実行の信頼性が求められる処理
まとめ
- KotlinのTimerは
java.util.Timerが正体 - ラムダ記法は
kotlin.concurrentの拡張関数 - Timerは 単一スレッド・例外に極端に弱い
- 実務では Coroutine または Executor系 が推奨
以上、KotlinのTimerについてでした。
最後までお読みいただき、ありがとうございました。










