Javaで簡単なタイマーを作成する方法がわかりません。私がする必要があるのは、本当に時間を表示することだけです。つまり、開始メソッドだけで、0:00、0:01、0:02 などのようにカウントアップし続けます。これに関する他の同様のフォーラム投稿を見たことがありますが、すべてのコードは私のレベルでは複雑です。理解; 私はJavaが初めてです。しかし、そのような基本的な機能を実行するだけのタイマーを作成するのはそれほど難しいことではありませんか? 誰かがそれを助けることができれば、それは大歓迎です:)
4 に答える
これは難しくありません。ただし、スタック オーバーフローに関するいくつかの非常に混乱した回答を見たことがあることに注意してください。場合によっては、コーディングの習慣が驚くほど悪いため、十分に注意してください。まず、質問に答えさせてください。
プログラマーがタイマーを実装する際に犯す最大の間違いは、現在の時間を追跡するために何かが必要だと考えることです。つまり、変数を毎秒インクリメントするある種のループ、またはそのようなばかげたことを記述します。時間を追跡するためにコードを記述する必要はありません。関数System.currentTimeMillis()
はそれを行いますが、非常に正確に行います。
タイマー コードには、多くのプログラマーが混同する 2 つの側面が含まれます。
- 時間の計算
- ディスプレイのリフレッシュ
表示する時間を計算するために必要なことは、タイマーが開始された時間を記録することだけです。
long startTime = System.currentTimeMillis();
後で時間を表示したい場合は、現在の時間からこれを差し引くだけです。
long elapsedTime = System.currentTimeMillis() - startTime;
long elapsedSeconds = elapsedTime / 1000;
long secondsDisplay = elapsedSeconds % 60;
long elapsedMinutes = elapsedSeconds / 60;
//put here code to format and display the values
プログラマーが犯す最大の間違いは、現在の時刻を保持する変数が必要だと考え、その変数を毎秒インクリメントするコードを書くことです。たとえば、「elapsedSeconds」と呼ばれるものを維持します。問題は、毎秒呼び出されるようにコードをスケジュールできることですが、そのコードが呼び出される正確な時間の保証はありません。システムがビジー状態の場合、そのコードは 2 番目よりかなり遅れて呼び出される可能性があります。システムが非常にビジーな場合 (たとえば、障害のあるディスクからのページのフェッチなど)、実際には数秒遅れる可能性があります。Thread.sleep(1000) 関数を使用して毎秒ループするコードでは、時間の経過とともにエラーが増加することがわかります。sleep が 300 ミリ秒遅れて 1 回返された場合、そのエラーは現在時刻の計算に組み込まれます。これはすべて完全に不要です。現在時刻を教えてくれる機能があります。
上記の計算は、このコードを毎秒、1 秒に 100 回、または 3.572 秒に 1 回実行しても正確です。要点はcurrentTimeMillis()
、このコードが呼び出されるタイミングに関係なく、時間を正確に表現することです。これは重要な考慮事項です。スレッドとタイマーのイベントは、特定の時間に正確であることが保証されていないためです。
タイマーの 2 番目の側面は、表示の更新です。これは、表示に使用しているテクノロジーによって異なります。GUI 環境では、ペイント イベントをスケジュールする必要があります。これらのペイント イベントは、表示が変更されると予想される時刻の直後に発生させたいと考えています。ただし、注意が必要です。ペイント イベントを要求することはできますが、処理する前に何百もの他のペイント イベントがキューに入れられている可能性があります。
これを行う怠惰な方法の 1 つは、1 秒あたり 10 回の描画イベントをスケジュールすることです。時間の計算は、特定の時点で呼び出されるコードに依存せず、同じ時間で画面を再描画しても問題にならないため、このアプローチにより、表示される時間が多かれ少なかれ保証されます。約 1/10 秒以内に正しい時刻を表示します。10 回中 9 回は既に画面に表示されているものをペイントしているため、これは少し無駄に思えます。
1 秒間に 30 回画面を更新するある種のアニメーション (ゲームなど) を含むプログラムを作成している場合は、何もする必要はありません。タイマー表示呼び出しを通常の画面更新に組み込むだけです。
描画イベントが高価な場合、または端末スタイルの出力を行うプログラムを作成している場合は、表示が変更されるまでの残り時間を計算することで、イベントのスケジューリングを最適化できます。
long elapsedTime = System.currentTimeMillis() - startTime;
long timeTillNextDisplayChange = 1000 - (elapsedTime % 1000);
変数 timeTillNextDisplayChange は、タイマーの秒部分が変更されるまで待機する必要があるミリ秒数を保持します。次に、その時点でペイント イベントが発生するようにスケジュールし、おそらく呼び出しThread.sleep(timeTillNextDisplayChange)
てスリープ後に出力を実行できます。コードがブラウザーで実行されている場合は、この手法を使用してページ DOM を適切なタイミングで更新できます。
この表示リフレッシュの計算には、タイマー自体の精度に影響を与えるものは何もないことに注意してください。スレッドは 10 ミリ秒遅れて、または 500 ミリ秒遅れてスリープから復帰する可能性があり、タイマーの精度は影響を受けません。すべてのパスで、currentTimeMillis から待機時間を計算するため、ある機会に遅れて呼び出されても、その後の表示が遅くなることはありません。
それが正確なタイマーの鍵です。OS が要求したとおりにルーチンを呼び出したり、ペイント イベントを送信したりすることを期待しないでください。もちろん、通常、最新のマシンでは、OS は非常に応答性が高く正確です。これは、他にあまり実行していないテスト状況で発生し、タイマーが機能しているように見えます。しかし、本番環境では、まれにストレスがかかる状況では、システムがビジーであるため、タイマーが「ドリフト」することは望ましくありません。