編輯:雖然我確實同意這個問題的關鍵取決於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