曾宏:监控中误用Timer导致进程无限增多的问题

案例详情

 

后台任务引入系统监控,所有客户端配置、代码修改、服务器端配置都做好后,启动任务,任务可以正常处理数据,监控视图也是正确的。

 

但是当任务结束退出后,发现进程依然被占用。执行多少次任务,就多了多少个进程。尤其在定时任务中问题尤为明显。

 

错误分析

 

实际上这是系统监控的一个bug

 

comsat对非web应用的监控要求在main函数里加上如下代码段,使用编程方式启动collector线程:

Launcher launcher = new Launcher();

launcher.startup();

 

launcher.startup()使用到了Timer实现定时操作(定时将监控数据发送到服务器端):

private static final Timer timer = new Timer();

timer.scheduleAtFixedRate(proxy, firstTime, period);

即使任务已经跑完,进程依然不会终止,因为Timer线程不是守护线程,在main函数执行完后它依然会继续调度执行。

 

正确用法

 

方法1

修改 private static final Timer timer = new Timer();

为   private static final Timer timer = new Timer(true);

设置Timer线程为守护线程。

 

方法2

使用ScheduledThreadPoolExecutor代替Timer

ScheduledThreadPoolExecutor executor

= new ScheduledThreadPoolExecutor(corePoolSize,new DaemonThreadFactory(true))

 

以上两种方法都是将定时操作设置为守护线程。

推荐使用方法2jdk1.5后,scheduledThreadPoolExecutor作为定时任务的调度器,完全可以代替问题多多的Timer

平台后续会发布comsat新版本,使用方法2解决这个bug(所以目前任务最好暂不引入comsat监控)。

 

其它说明

 

1daemon线程与 non-daemon线程的区别:

daemon线程,就是一种“在背景提供通用性服务”的线程,它并不属于程序本体。因此,当所有non-daemon线程结束生命,程序也就终止了。如果有任何non-daemon线程还在执行,程序(也就是main()的那个线程)就不能终止。

2TimerScheduledThreadPoolExecutor区别

1Timer对调度的支持是基于绝对时间的,因此任务对系统时间的改变是敏感的;而ScheduledThreadPoolExecutor支持相对时间。

2Timer使用单线程方式来执行所有的TimerTask,如果某个TimerTask很耗时则会影响到其他TimerTask的执行;而ScheduledThreadPoolExecutor则可以构造一个固定大小的线程池来执行任务。

3Timer不会捕获由TimerTask抛出的未检查异常,故当有异常抛出时,Timer会终止,导致未执行完的TimerTask不再执行,新的TimerTask也不能被调度;ScheduledThreadPoolExecutor对这个问题进行了妥善的处理,不会影响其他任务的执行。

你可能感兴趣的:(编程,Web,应用服务器)