web-dev-qa-db-ja.com

Javaに一貫した(単調な)Clockの実装はありますか?

デフォルトのJava.time.Clock実装は、System.currentTimeMillis()に基づいています。たとえばここで説明したように、 Javaで単調に増加する時間? 、単調であるとは限りません。

実際、私は定期的に、システム時刻が数秒前に自動的に調整され、Javaクロックも元に戻るという状況を経験しています。

//now() returns 2016-01-13T22:34:05.681Z
order.setCreationTime(Instant.now());

//... something happens, order gets cancelled

//now() returns 2016-01-13T22:34:03.123Z
//which is a few seconds before the former one,
//even though the call was performed later - in any reasonable sense.
//The recorded history of events is obviously inconsistent with the real world.
order.setCancelationTime(Instant.now());

その場合、一方向にのみ進む時間に頼ることができない場合、イベント履歴の記録や分析など、時間に敏感なことを実行することは不可能です。

前述の投稿によると、System.nanoTime()は単調です(基盤となるシステムがサポートしている場合)。したがって、Java.time APIに基づいてコードを作成する場合は、時間の一方通行を確保するために、内部でnanoTimeを使用するClockが必要になります。たぶん、このようなものがうまくいくでしょう。それともそうではありませんか?

public class MyClock() extends Java.time.Clock {

    private final long adjustment;

    public MyClock() {
        long cpuTimeMillis = System.nanoTime()/1000000;
        long systemTimeMillis = System.currentTimeMillis();
        adjustment = systemTimeMillis - cpuTimeMillis;
    }

    @Override
    public long millis() {
        long currentCpuTimeMillis = System.nanoTime()/1000000;
        return currentCpuTimeMillis + adjustment;
    }
}   

これは単なるスケッチであり、完全なClockの実装ではありません。また、適切な実装では、currentTimeMillis()に対して直接ではなく、コンストラクターで渡された別のClockに対しても調整を実行する必要があると思います。

または、そのような単調なClockの実装はどこでも利用できますか?同じ問題に直面している人は多かったに違いないと思います。

結論

刺激的なコメントと回答をありがとう。コメントには興味深い点がいくつか散らばっているので、ここで要約します。

1。単調時計

私の最初の質問に関しては、はい、システム時刻が逆方向にジャンプすることによって影響を受けない単調なクロックを持つことは可能です。このような実装は、上記で提案したようにSystem.nanoTime()に基づくことができます。過去にはこのアプローチに問題がありましたが、今日の最新のシステムでは問題なく機能するはずです。このアプローチは、たとえば Time4J ライブラリにすでに実装されており、単調な時計はJava.time.Clockに簡単に変換できます。

Clock clock = TemporalType.CLOCK.from(SystemClock.MONOTONIC);

2。適切なシステム時間制御

システム時刻管理(unix/linuxのntpd)を構成して、システム時刻が実質的に戻らないようにすることができます(必要に応じて速度が低下するだけです)。システム時刻が単調であり、クロックがないことを信頼できます。 Javaでは魔法が必要です。

私のアプリはサーバー側であり、時間を制御できるので、この方法を使用します。実際、私は自分でインストールした実験環境(ドメインの表面的な知識のみ)で異常を経験し、ntpdateクライアントのみを使用していました(これは、 ntpdではなく(時間は同期していません)(ジャンプバックしないように構成できます)。

3。クロックではなくシーケンスを使用する

何が何の前に起こったかという強い関係を追跡する必要がある場合、壁時計に依存せずに、原子的に生成されたシーケンスからイベントにシリアル番号を与える方が安全です。アプリケーションが複数のノードで実行されると、これが唯一のオプションになります(ただし、私の場合はそうではありません)。

26
Jan X Marek

@ the8472が言うように、重要なのは、マシン(jvmが実行されている場所)の時刻同期を正しくすることです。

クライアントをプログラムする場合、システムクロックに依存することは本当に危険です。ただし、サーバーの場合は解決策があります。厳密な構成でNTPを使用することを検討することをお勧めします。

ここで 彼らは基本的に [〜#〜] ntp [〜#〜] は時間を遅くし、逆に設定しないと説明しています。

そして これNTPドキュメント は言う:

場合によっては、特にntpdを最初に起動したときに、エラーが128ミリ秒を超えることがあります。これにより、ローカルクロック時間がサーバーに対して将来128秒を超える場合、クロックが逆方向に設定されることがあります。一部のアプリケーションでは、この動作は受け入れられない場合があります。コマンドラインに-xオプションが含まれている場合、クロックはステップされず、スルー補正のみが使用されます。

5
Milos Gregor

NanoTimeは単調に増加する可能性がありますが、壁時間とはあまり関係がないことに注意してください。休止状態イベントのため、VM停止など。

また、複数のサーバーに物事を分散し始めると、currentMillisで同期すると、クロックドリフトのために再び噛み付く可能性があります。

たぶん、サーバーのシステム時間を制御することを検討する必要があります。

または、イベントが記録されたと思われる時間とは別に、イベントの相対的な順序を追跡します。

2
the8472