- 学习线程的时候,做了一个按秒显示当前时间的小程序。这个程序不难,
 但提到这个小程序有缺陷,就是你仔细盯着输出结果看,有时候会发现显示
 的时间突然跳了一秒,比如从19:56:42 跳到19:56:44。
 我对这个现象产生的原因很感兴趣,于是研究了一下它是如何产生的以及研究了一下解决办法。
 原程序如下:
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 
 public class ThreadDemo8 {
 
 /**
 * @param args
 */
 public static void main(String[] args) {
 /*
 * 实现电子表功能
 * 每秒钟输出依次当前系统时间
 * HH:mm:ss
 */
 Thread t=new Thread(){
 public void run(){
 Calendar calendar;
 Date date;
 String str;
 SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
 for(;;){
 calendar=Calendar.getInstance();
 calendar.add(calendar.HOUR_OF_DAY, 16);
 calendar.add(calendar.MINUTE, 4);
 date=calendar.getTime();
 str=sdf.format(date);
 System.out.println(str+" ");//输出当前时间
 try{
 Thread.sleep(1000);
 }catch(InterruptedException e){
 }
 }
 }
 };
 t.start();
 }
 }
 
 为了使时间显示的更精确,我将用于输出当前时间的 System.out.println(str+" ");
 这句改为了 System.out.println(str+" "+System.currentTimeMillis()%1000);
 输出结果如下:
 20:09:43 998
 20:09:44 998
 20:09:45 998
 20:09:46 999
 20:09:47 999
 20:09:48 999
 20:09:49 999
 20:09:50 999
 20:09:52 0
 20:09:53 0
 20:09:54 0
 20:09:55 0
 20:09:56 1
 20:09:57 1
 20:09:58 1
 20:09:59 1
 20:10:00 2
 20:10:01 2
 20:10:02 2
 
 可以发现,在20:09:50到20:09:52发生了跳跃,把20:09:51隔过去了。还能发现大约每数4.4次,
 毫秒数会+1,也就是每4.4*1000=4400次即73分钟会跳一次秒。这是不是很像每天比实际地球自转
 时间多算约4分钟,所以每4年要多1天来抵消这个误差?这个计时程序的误差来自
 Thread.sleep(1000); 这里没有把计算机运行一次for循环所用的时间考虑进去。所以,它应
 该sleep更少的时间才对,大约少sleep 1/4.4毫秒。
 于是,我就像用增加润年的方法把
 Thread.sleep(1000); 改为:
 Thread.sleep(1000-i%5/4+i%40/39); //每5毫秒减少1毫秒,每40毫秒增加1毫秒
 它的输出结果变成这样了
 20:25:36 393
 20:25:37 393
 20:25:38 394
 20:25:39 394
 20:25:40 394
 20:25:41 393
 20:25:42 393
 20:25:43 394
 20:25:44 394
 20:25:45 394
 20:25:46 393
 20:25:47 394
 20:25:48 394
 误差小了很多,但是还是不够精确。就像我们现实的时间虽有润年调整,还是无法和地球自转时间对照,
 因此前几年有2天都出现了如19:59:60这样时间,增加1秒以对地球的计时做校正。
 但是,在这个程序中我们可以抛弃这种永远无法真正校准时间的方法。我想起了原来我学过的一门课程
 《自动控制原理》,里面有个概念叫做负反馈,根据结果来校正下一次的结果可以做到真正的精准。
 用在这里具体就是:输出的时间超了1毫秒就在下次减1毫秒,超2毫秒就减2毫秒。反正它能自动调整为
 准确时间
 于是有了如下方法:
 Thread.sleep(1000); 改为:
 Thread.sleep(1000-System.currentTimeMillis()%1000);
 输出结果如下:
 20:32:32 0
 20:32:33 0
 20:32:34 0
 20:32:35 0
 20:32:36 0
 20:32:37 0
 20:32:38 0
 可以发现,这次的输出时间都是和系统几乎同步的!做到了真正的精准。最终版本的程序如下:
 
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 
 public class ThreadDemo8 {
 
 /**
 * @param args
 */
 public static void main(String[] args) {
 /*
 * 实现电子表功能
 * 每秒钟输出依次当前系统时间
 * HH:mm:ss
 */
 Thread t=new Thread(){
 public void run(){
 Calendar calendar;
 Date date;
 String str;
 SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
 for(int i=0;;i++){
 calendar=Calendar.getInstance();
 calendar.add(calendar.HOUR_OF_DAY, 16);
 calendar.add(calendar.MINUTE, 4);
 date=calendar.getTime();
 str=sdf.format(date);
 if(i>0){
 System.out.println(str+" "+System.currentTimeMillis()%1000);
 }
 try{
 Thread.sleep(1000-System.currentTimeMillis()%1000);
 }catch(InterruptedException e){
 }
 }
 }
 };
 t.start();
 }
 
 }
