Java 排程器延時存在誤差,是否有更精確的排程方法?Alex Wang2017-08-29 22:09:15

首先,複習一把Java中的時間單位:

* 1毫秒=1ms=1/1000秒,表達方式TimeUnit。MILLISECONDS

* 1微秒=1μs=1/1000毫秒,表達方式TimeUnit。MICROSECONDS

* 1納秒=1ns=1/1000微秒,表達方式TimeUnit。NANOSECONDS

然後,我猜測你是由於時間單位直接使用毫秒,沒有使用最精確的納秒導致的,於是我寫了一個程式檢測:

public

class

TimeTest

{

static

final

long

PER_NANO

=

1000

*

1000

//每毫秒為1000*1000納秒

public

static

void

main

String

[]

args

{

final

ScheduledExecutorService

scheduler

=

Executors

newScheduledThreadPool

1

);

final

long

times

[]

=

new

long

100

];

//用來存放時間的陣列

Worker

worker

=

new

Worker

times

);

//重複任務,延遲100ms開始,間隔100ms

final

ScheduledFuture

<?>

beeperHandle

=

scheduler

scheduleAtFixedRate

worker

100

100

TimeUnit

MILLISECONDS

);

//延遲任務,延遲10000ms執行。取消重複任務,輸出結果

scheduler

schedule

new

Runnable

()

{

public

void

run

()

{

beeperHandle

cancel

true

);

long

max

=

0

for

int

i

=

1

i

<

times

length

i

++)

{

long

diff

=

times

i

-

times

i

-

1

];

max

=

Math

max

max

diff

);

System

out

println

diff

);

}

double

maxdiff

=

((

double

max

/

PER_NANO

-

100

System

out

println

“max = ”

+

maxdiff

+

“ ms”

);

scheduler

shutdown

();

}

},

10000

*

PER_NANO

TimeUnit

NANOSECONDS

);

}

//為了防止IO佔用時間,把時間先存入陣列,最後再一次輸出

static

class

Worker

implements

Runnable

{

private

static

int

number

//序號

private

final

long

times

[];

//時間陣列

Worker

long

times

[])

{

this

times

=

times

}

@Override

public

void

run

()

{

times

number

++]

=

System

nanoTime

();

}

}

}

多次執行結果如下(單位為納秒,省略了前面的輸出):

max = 0。9890390000000053 ms

max = 0。984603000000007 ms

max = 0。991429999999994 ms

我發現誤差沒有超過2ms的。

我修改了時間單位為納秒:

final

ScheduledFuture

<?>

beeperHandle

=

scheduler

scheduleAtFixedRate

worker

100

*

1000

*

1000

100

*

1000

*

1000

TimeUnit

NANOSECONDS

);

多次執行結果如下(單位為納秒,省略了前面的輸出):

max = 0。993819000000002 ms

max = 0。9982550000000003 ms

max = 7。663642999999993 ms

終於出現了一個7ms的。因此我感覺靠使用納秒為時間單位,並沒有什麼幫助。

更好的方法,還沒想到:(

Java 排程器延時存在誤差,是否有更精確的排程方法?QX Geng2017-08-30 07:51:50

Java不咋熟,不過看到10ms的誤差,這個明顯是作業系統排程的時間片,也就是系統那裡排程執行緒的時間精度是10ms。

Win下有個Win32APItimeBeginPeriod,可以把時間片設定成1ms。這樣誤差應該能控制在1ms左右

再有,如果定時的回撥邏輯能確保在觸發間隔內完成,就是自己起個執行緒不停輪詢時間。

最後,非實時作業系統,沒有很精確的定時器,尤其是現在的搶佔式多工,執行緒的執行延遲是未知的。

Java 排程器延時存在誤差,是否有更精確的排程方法?gavin2017-08-30 16:57:15

定時器的精度跟作業系統跟硬體有關係。linux支援高精度納秒級喚醒,windows只支援毫秒級喚醒,所以我猜測你用的是windows系統,你可以在linux上試試看,另外將執行緒的優先順序定的高一些,以便喚醒後能更快得到CPU資源。

Java 排程器延時存在誤差,是否有更精確的排程方法?王輝2017-09-01 00:50:25

換linux伺服器,我們線上跑的應用裡面的定時器十天都不會差1ms。

Java 排程器延時存在誤差,是否有更精確的排程方法?碼農甲2017-09-15 08:17:40

用java就別就糾結這10毫秒了,一個gc,全世界都安靜了。即便是非gc語言,還涉及到作業系統的排程