How can I measure time in Java not susceptible to System clock changes?
I would like to measure elapsed time in a Java. However differences in System.currentTimeMillis()
and (I believe) System.nanoTime()
can be changed by external changes eg someone (or the system) altering the system clock.
Using network calls is not an option as it's possible very frequent and fast returns are required.
Is there a common solution for this?
EDIT
Sorry, I should have elaborated about the reason. It isn't to stop malicious users - it's things like client initiated logout for being idle and r开发者_如何学Coutine client events.
This doesn't really answer your question, but bug #6458294 implies that where possible, Sun's implementation of nanoTime() will use mechanisms which are truly monotonic (CLOCK_MONOTONIC on Linux, QueryPerformanceFrequency/QueryPerformanceCounter on Windows). Only if these are unavailable will it fall back to a mechanism which is susceptible to system clock changes.
If you have control (or at least knowledge) of the hardware you're running on, and can ensure that these clock mechanisms will be available, you might be in luck and nanoTime() will do fine.
You may also like to read this blog post, which discusses the HotSpot-on-Windows case in more detail.
I don't think there is a way to do this.
There is certainly no way to do this that cannot be subverted. Fundamentally, you are at the mercy of the operating system and the JVM as to what is reported to the Java app as the current time. Either or both of these could be patched so that the Java code ends up getting a bogus timestamp value. You could try to defend against this, but then all the hacker needs to do is to patch your app to disable license checking entirely.
For the record, this "vulnerability" applies whether or not you are using Java.
If you're trying to stop people from subverting a license scheme by setting the clock back, you need to store the highest time you've seen so far in some sort of encrypted and secure storage, and then disable the program if either the time goes less than the highest you've seen (with some allowance for NTP clock adjustments and DST changes of course), or if they somehow tamper with or delete the secure store.
I don't know if nanoTime() is likely to change if the system clock changes, but I suppose it's possible. Also nanoTime() may not be accurate.
If you really need to guard against clock changes, you could monitor the clock in a thread. Sleep for 100ms or 1000ms, then call currentTimeMillis(). If clock advanced more then 1000 + x or has gone backwards then likely the clock changed (or the thread got hung up on something, which is possible).
In the event of a disjunction, you could actually make the network call to check. Of course, it's possible that network time might change due to the insertion of leap seconds. I once read a Slashdot comment by some scientists who was coordinating astronomical data from lots of different sources. A leap second was added during his experiment, and it basically ruined it because some sites inserted it and others didn't.
Another possibility might be to use a low level native API to get some other system timers. Such as system or network uptime to calibrate the API. Windows has the getTickCount() function that returns the number of milliseconds since boot. On unix systems you can use the uptime command to get a rough estimate. You can periodically check these values to see if the system clock has changed.
If you don't mind adding a bit of native code to your Java app, use:
QueryPerformanceCounter()
andQueryPerformanceFrequency()
on Windows; orPOSIX
clock_gettime()
function withCLOCK_MONOTONIC
clock ID.
Note that the use of the x86 TSC register is discouraged on multiprocessor systems, so you better use the above APIs.
精彩评论