编辑:虽然我确实同意这个问题的关键取决于Thread.sleep()的准确性,但我一直相信Thread.sleep()对睡眠的偏见超过了所要求的时间。为什么线程在睡眠持续时间到期之前恢复?我可以理解操作系统调度程序不能及时回到线程来唤醒它,但为什么它会早点到达那里呢?如果操作系统可以任意提早唤醒它们,睡眠线程的意义何在?具有显着抖动和错误的多线程时序应用程序?
我正在尝试编写一个类,以在我的项目中执行模块化计时。这个想法是让一个类能够测量我感兴趣的任何特定代码的执行时间。我想做这个测量,而不必在原地编写特定的时间代码,并提供一个干净的模块化界面。
这个概念是建立在一个教练身上,每个跑步者都有多个秒表。我可以用不同的秒表ID来调用一个类来创建测量它们各自相对执行时间的线程。此外,还有一个搭接功能可以测量手表时钟的子区间。该实现集中在Stopwatch(coach)类和Watch(runner)类的使用HashMap。
这是我实现:
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class Stopwatch {
private static Map<String, Watch> watchMap = new HashMap<>();
public static boolean start(String watchID) {
if(!watchMap.containsKey(watchID)) {
watchMap.put(watchID, new Watch());
return true;
} else {
return false;
}
}
public static void stop(String watchID) {
if(watchMap.containsKey(watchID)) {
watchMap.get(watchID).stop();
}
}
public static void startLap(String watchID, String lapID) {
if(watchMap.containsKey(watchID)) {
watchMap.get(watchID).startLap(lapID);
}
}
public static void endLap(String watchID, String lapID) {
if(watchMap.containsKey(watchID)) {
watchMap.get(watchID).stopLap(lapID);
}
}
public static void stopAndSystemPrint(String watchID) {
if(watchMap.containsKey(watchID)) {
Watch watch = watchMap.get(watchID);
if(watch.isRunning()) {
watch.stop();
}
Map<String, Long> lapMap = watch.getLapMap();
System.out.println("/****************** " + watchID
+ " *******************\\");
System.out.println("Watch started at: " + watch.getStartTime()
+ " nanosec");
for(Entry<String, Long> lap : lapMap.entrySet()) {
System.out.println("\t" + lap.getKey() + ": "
+ ((double)lap.getValue()/1000000.0)
+ " msec");
}
System.out.println("Watch ended at: " + watch.getEndTime()
+ " nanosec");
System.out.println("Watch total duration: "
+ (double)(watch.getDuration()/1000000.0)
+ " msec");
System.out.println("\\****************** " + watchID
+ " *******************/\n\n");
}
}
private static class Watch implements Runnable {
private Thread timingThread;
private long startTime;
private long currentTime;
private long endTime;
private volatile boolean running;
private Map<String, Long> lapMap;
public Watch() {
startTime = System.nanoTime();
lapMap = new HashMap<>();
running = true;
timingThread = new Thread(this);
timingThread.start();
}
@Override
public void run() {
while(isRunning()) {
currentTime = System.nanoTime();
// 0.5 Microsecond resolution
try {
Thread.sleep(0, 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
running = false;
endTime = System.nanoTime();
}
public void startLap(String lapID) {
lapMap.put(lapID, currentTime);
}
public void stopLap(String lapID) {
if(lapMap.containsKey(lapID)) {
lapMap.put(lapID, currentTime - lapMap.get(lapID));
}
}
public Map<String, Long> getLapMap() {
return this.lapMap;
}
public boolean isRunning() {
return this.running;
}
public long getStartTime() {
return this.startTime;
}
public long getEndTime() {
return this.endTime;
}
public long getDuration() {
if(isRunning()) {
return currentTime - startTime;
} else {
return endTime - startTime;
}
}
}
}
而且,这里是我使用来测试这个实现的代码:
public class StopwatchTest {
public static void main(String[] args) throws InterruptedException {
String watch1 = "watch1";
Stopwatch.start(watch1);
String watch2 = "watch2";
Stopwatch.start(watch2);
String watch3 = "watch3";
Stopwatch.start(watch3);
String lap1 = "lap1";
Stopwatch.startLap(watch1, lap1);
Stopwatch.startLap(watch2, lap1);
Thread.sleep(13);
Stopwatch.endLap(watch1, lap1);
String lap2 = "lap2";
Stopwatch.startLap(watch1, lap2);
Thread.sleep(500);
Stopwatch.endLap(watch1, lap2);
Stopwatch.endLap(watch2, lap1);
Stopwatch.stop(watch3);
String lap3 = "lap3";
Stopwatch.startLap(watch1, lap3);
Thread.sleep(5000);
Stopwatch.endLap(watch1, lap3);
Stopwatch.stop(watch1);
Stopwatch.stop(watch2);
Stopwatch.stop(watch3);
Stopwatch.stopAndSystemPrint(watch1);
Stopwatch.stopAndSystemPrint(watch2);
Stopwatch.stopAndSystemPrint(watch3);
}
}
最后,输出这个测试可以产生:
/****************** watch1 *******************\
Watch started at: 45843652013177 nanosec
lap1: 12.461469 msec
lap2: 498.615724 msec
lap3: 4999.242803 msec
Watch ended at: 45849165709934 nanosec
Watch total duration: 5513.696757 msec
\****************** watch1 *******************/
/****************** watch2 *******************\
Watch started at: 45843652251560 nanosec
lap1: 4.5844165436787E7 msec
Watch ended at: 45849165711920 nanosec
Watch total duration: 5513.46036 msec
\****************** watch2 *******************/
/****************** watch3 *******************\
Watch started at: 45843652306520 nanosec
Watch ended at: 45849165713576 nanosec
Watch total duration: 5513.407056 msec
\****************** watch3 *******************/
这段代码有一些有趣的(对我来说,至少)结果。
一,手表是在1毫秒量级的早晚完成的。尽管纳秒时钟有些不准确,但我会认为,我可以获得比1毫秒更好的精度。也许我忘了一些关于有效数字和准确性的事情。
另一个是,在这个测试结果,watch2
完成对这一结果其圈:
Watch started at: 45843652251560 nanosec
lap1: 4.5844165436787E7 msec
Watch ended at: 45849165711920 nanosec
我检查我是操纵在我stopAndSystemPrint
法的价值的方式,但是这似乎并不对错误有任何影响。我只能得出结论,我在那里做的数学是可靠的,而且之前的某些事情有时会被打破。有时候我有点担心,因为 - 我认为 - 它告诉我,我可能在Watch
课程中对我的线程做错了什么。看来,单圈持续时间正在被抛出,并导致我的开始时间和结束时间之间的某个值。
我不确定这些问题是排他性的,但如果我必须选择一个来解决,那就是抖动。
有人可以做出正面或反面的原因,为什么会有1ms的抖动?
奖励:为什么手表会不时出现圈数不一致?
可能的重复[Thread.sleep有多准确?](http://stackoverflow.com/questions/18736681/how-accurate-is-thread-sleep) – Basilevs