1. 概要

この記事では、Javaで経過時間を測定する方法を見ていきます。 これは簡単に聞こえるかもしれませんが、注意しなければならないいくつかの落とし穴があります。

経過時間を測定する機能を提供する標準のJavaクラスと外部パッケージについて説明します。

2. 簡単な測定

2.1. currentTimeMillis()

Javaで経過時間を測定する必要がある場合は、次のように試みることができます。

long start = System.currentTimeMillis();
// ...
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;

コードを見ると、それは完全に理にかなっています。 開始時にタイムスタンプを取得し、コードの終了時に別のタイムスタンプを取得します。 経過時間は、これら2つの値の差です。

ただし、 System.currentTimeMillis()が壁掛け時計の時間を測定するため、結果は不正確になる可能性があります。壁掛け時計の時間は、さまざまな理由で変更される可能性があります。 システム時刻を変更すると、結果に影響を与える可能性があります。または、うるう秒によって結果が中断されます。

2.2. nanoTime()

java.lang.System クラスの別のメソッドは、 nanoTime()です。 Javaのドキュメントを見ると、次のステートメントが見つかります。

「この方法は経過時間を測定するためにのみ使用でき、システムや壁掛け時計の時間の他の概念とは関係ありません。」

それを使用しましょう:

long start = System.nanoTime();
// ...
long finish = System.nanoTime();
long timeElapsed = finish - start;

コードは基本的に以前と同じです。 唯一の違いは、タイムスタンプの取得に使用されるメソッド– currentTimeMillis()の代わりに nanoTime()です。

nanoTime()は、明らかに、時間をナノ秒単位で返すことにも注意してください。 したがって、経過時間が別の時間単位で測定される場合は、それに応じて変換する必要があります。

たとえば、ミリ秒に変換するには、ナノ秒単位の結果を1.000.000で割る必要があります。

nanoTime()のもう1つの落とし穴は、はナノ秒の精度を提供しますが、ナノ秒の解像度を保証しないことです(つまり、 値が更新される頻度)。

ただし、解像度が少なくとも currentTimeMillis()の解像度と同じになることを保証します。

3. Java 8

Java 8を使用している場合は、新しい java.time.Instantクラスとjava.time.Durationクラスを試すことができます。 どちらも不変でスレッドセーフであり、新しい java.time API内のすべてのクラスと同様に、独自のタイムスケールJavaタイムスケールを使用します。

3.1. Javaタイムスケール

時間を測定する従来の方法は、1日を60秒の60分の24時間に分割することです。これにより、1日は86.400秒になります。 ただし、太陽の日は必ずしも同じくらい長いとは限りません。

UTCタイムスケールでは、実際には1日を86.399または86.401SI秒にすることができます。 SI秒は科学的な「標準国際秒」であり、セシウム133原子の放射期間によって定義されます。 これは、日を太陽に合わせるために必要です。

Java Time-Scaleは、各暦日を秒と呼ばれる正確に86.400の細分に分割します。 うるう秒はありません。

3.2. インスタントクラス

Instant クラスは、タイムライン上のインスタントを表します。 基本的には、 1970-01-01T00:00:00Zの標準Javaエポック以降の数値タイムスタンプです。

現在のタイムスタンプを取得するには、 Instant.now()静的メソッドを使用できます。 このメソッドでは、オプションのClockパラメーターを渡すことができます。 省略した場合、デフォルトのタイムゾーンのシステムクロックを使用します。

前の例のように、開始時刻と終了時刻を2つの変数に格納できます。次に、両方の瞬間の間の経過時間を計算できます。

さらに、 Durationクラスとそのbetween()メソッドを使用して、2つのInstantオブジェクト間の期間を取得できます。 最後に、durationをミリ秒に変換する必要があります。

Instant start = Instant.now();
// CODE HERE        
Instant finish = Instant.now();
long timeElapsed = Duration.between(start, finish).toMillis();

4. ストップウォッチ

ライブラリに移ると、Apache Commons Langは、経過時間を測定するために使用できるStopWatchクラスを提供します。

4.1. Mavenの依存関係

pom.xmlを更新することで、最新バージョンを入手できます。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

依存関係の最新バージョンはここで確認できます。

4.2. StopWatchで経過時間を測定する

まず、クラスのインスタンスを取得する必要があります。次に、経過時間を簡単に測定できます。

StopWatch watch = new StopWatch();
watch.start();

ウォッチを実行したら、ベンチマークするコードを実行し、最後に stop()メソッドを呼び出すだけです。 最後に、実際の結果を取得するために、 getTime()を呼び出します。

watch.stop();
System.out.println("Time Elapsed: " + watch.getTime()); // Prints: Time Elapsed: 2501

StopWatchには、測定を一時停止または再開するために使用できるいくつかの追加のヘルパーメソッドがあります。これは、ベンチマークをより複雑にする必要がある場合に役立つことがあります。

最後に、このクラスはスレッドセーフではないことに注意してください。

5. 結論

Javaで時間を測定する方法はたくさんあります。 currentTimeMillis()を使用して、非常に「従来の」(そして不正確な)方法を説明しました。 さらに、ApacheCommonのStopWatch を確認し、Java8で利用可能な新しいクラスを確認しました。

全体として、経過時間を簡単かつ正確に測定するには、 nanoTime()メソッドで十分です。 また、 currentTimeMillis()よりも入力が短くなっています。

ただし、適切なベンチマークを行うために、時間を手動で測定する代わりに、Java Microbenchmark Harness(JMH)のようなフレームワークを使用できることに注意してください。 このトピックはこの記事の範囲を超えていますが、ここについて説明しました。

最後に、いつものように、ディスカッション中に使用されたコードは、GitHubにあります。