LoginSignup
5
2

More than 1 year has passed since last update.

System.currentTimeMillis() と System.nanoTime() の違い

Last updated at Posted at 2022-10-30

概要

  • System.currentTimeMillis()
    • 取得できる値は、1970-01-01T00:00:00Z からのミリ秒(UNIX TIME)
    • 時刻の絶対値を取得する際に使用する
  • System.nanoTime()
    • 取得できる値は、任意の時点からのナノ秒
    • 時刻の相対値を取得する際に使用する

あと、Java 8 で使えるようになった Instant.now() も併せて解説します。

  • Instant.now()
    • 取得できる値は、1970-01-01T00:00:00Z からの秒 + ナノ秒(UNIX TIME)
    • 時刻の絶対値を取得する際に使用する

System.currentTimeMillis() の使い方

あるタイミングで現在時刻を取得するために使います。

long now = System.currentTimeMillis();
// ==> 1667115400695

一方で、以下のような相対時刻の取得には不向きです。

long start = System.currentTimeMillis();
// … 処理 …
long end = System.currentTimeMillis();

long total = end - start;

System.currentTimeMillis()不連続な時間の変化(例えば、システム管理者が時間を手動で変更した場合など)の影響を受けるためです。
例えば、上記の処理の途中で手動で時刻が変更されると、total が極端に大きくなったり負の値になったりします。

あくまで、 ある時点の時刻をミリ秒で取得する、という用途に限って使う API です。

System.nanoTime() の使い方

ある二つの時点の相対時間を取得するために使います。

long start = System.nanoTime();
// … 処理 …
long end = System.nanoTime();

long total = end - start;
// => 1180305853

System.nanoTime() は、不連続な時間の変化の影響を受けません。
上記の場合、処理の途中で時刻が変更されても、total が実際の処理時間を表す値になります。

一方で、あるタイミングの現在時刻を取得するのには不向きです。
例えば、System.nanoTime() を呼び出して 3761546426118 という値が返ってきても、これが何を意味するのか(いつからの経過時刻なのか)は不明です。

long now = System.nanoTime();
// => 3761546426118

あくまで、 ふたつの時刻の差分をナノ秒で取得する、という用途に限って使う API です。

Instant.now() の使い方

System.currentTimeMillis() と同様に、あるタイミングで現在時刻を取得するために使います。

java.time.Instant.now();
// ==> 2022-10-30T07:39:35.003041Z

Instant 型なので、ミリ秒を直接扱うのよりも扱いやすいです。
例えば、システムのタイムゾーンに変換して、それをフォーマットして出力する、ということも簡単にできます。

ZonedDateTime dateTime = now.atZone(ZoneId.systemDefault())
String text = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dateTime);
==> "2022-10-30T22:00:45.1861852+09:00"

また、System.currentTimeMillis() はミリ秒までの精度ですが、 Instant.now() はより細かい精度で時刻を扱います。
(Windows だと 100 ナノ秒です)

今となっては、System.currentTimeMillis() よりも Instant 型を使った方がいいと思います。

OpenJDK での実装の詳細

System.currentTimeMillis(), Instant.now()

OS API コード
Windows GetSystemTimeAsFileTime jdk/os_windows.cpp
上記以外(Linux など) clock_gettime(CLOCK_REALTIME, &ts) jdk/os_posix.cpp

System.nanoTime()

OS API コード
Windows QueryPerformanceCounter jdk/os_windows.cpp
macOS mach_absolute_time jdk/os_bsd.cpp
AIX mread_real_time jdk/os_aix.cpp
上記以外(Linux など) clock_gettime(CLOCK_MONOTONIC, &ts) jdk/os_posix.cpp

なお、 Linux 等で使われる CLOCK_MONOTONIC は adjtime(3) や NTP が行う段階的な調整の影響を受けるとのこと。
CLOCK_MONOTONIC_RAW のほうがいいのでは?という気がするのですが、こちらは Linux 固有(POSIX非対応)なので、Java では使っていないようです。

Java 考古学者向けの情報

System.currentTimeMillis() の分解能

Windows XP のころは、System.currentTimeMillis() が 16ms 程度の分解能しかないと言われていました。これは、過去 GetSystemTimeAsFileTime の分解能がそれぐらいだったせいです。
現在は 1ms の分解能になっています。1

以下のコードで、1ms の分解能が得られることを確認できます。
(Windows 10 で確認)

for(var i = 0; i < 1000; i++) {
    System.out.println(System.currentTimeMillis());
}
実行結果
…(中略)…
1667107783815
1667107783815
1667107783816
1667107783816
…(中略)…
1667107783817
1667107783817
1667107783818

Linux での実装の変化

昔は、 System.currentTimeMillis() の実装に gettimeofday を使っていました。
しかし、 Java 15 から clock_gettime が使える場合は、こちらが使われるようになりました。

また、現在のJava がサポートしている環境は clock_gettime が使えることが保証されていることが確認できたので、 Java 17 で gettimeofday を使う処理は削除されました。

  1. たぶん、Windows Vista のタイミングで? 調べたけど分からず…。

5
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2