在Timer和ScheduledExecutorService间决择

本文对比分析了Java Timer和ScheduledThreadPoolExecutor的区别与优势,重点介绍了如何利用后者避免Timer存在的问题,如任务延迟执行、周期性执行、异常处理等方面。通过示例代码展示了改进后的应用效果,确保了任务执行的稳定性和可靠性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

quartzWeb框架threadJDK

  java.util.Timer计时器有管理任务延迟执行("1000ms后执行任务")以及周期性执行("如每500ms执行一次该任务")

  但是,Timer存在一些缺陷,因此你应该考虑使用ScheduledThreadPoolExecutor作为代替品,Timer对调度的支持是基于绝对时间,而不是相对时间的,由此任务对系统时钟的改变是敏感的(也就是说你修改系统时间会对其造成影响);ScheduledThreadExecutor只支持相对时间。

 

   Timer的另一个问题在于,如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。

  Timer线程并不捕获异常,所以TimerTask抛出的未检查的异常会终止timer线程。

  这种情况下,Timer也不会再重新恢复线程的执行了;它错误的认为整个Timer都被取消了。此时,已经被安排但尚未执行的TimerTask永远不会再执行了,新的任务也不能被调度了。

public class TimerTest {
	private Timer timer = new Timer();

	// 启动计时器
	public void lanuchTimer() {
		timer.schedule(new TimerTask() {
			public void run() {
				throw new RuntimeException();
			}
		}, 1000 * 3, 500);
	}

	// 向计时器添加一个任务
	public void addOneTask() {
		timer.schedule(new TimerTask() {
			public void run() {
				System.out.println("hello world");
			}
		}, 1000 * 1, 1000 * 5);
	}

	public static void main(String[] args) throws Exception {
		TimerTest test = new TimerTest();
		test.lanuchTimer();
		Thread.sleep(1000 * 5);// 5秒钟之后添加一个新任务
		test.addOneTask();
	}
}
执行这之后的结果为:

Exception in thread "Timer-0" java.lang.RuntimeException
	at com.bill99.test.TimerTest$1.run(TimerTest.java:25)
	at java.util.TimerThread.mainLoop(Timer.java:512)
	at java.util.TimerThread.run(Timer.java:462)
Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled.
	at java.util.Timer.sched(Timer.java:354)
	at java.util.Timer.schedule(Timer.java:222)
	at com.bill99.test.TimerTest.addOneTask(TimerTest.java:32)
	at com.bill99.test.TimerTest.main(TimerTest.java:43)

可以看出先前的任务抛出了异常后面的任务无法再正常调度了。

 运行该程序,Timer会抛出一个RumtimeExceptionjava.lang.IllegalStateException:Timeralready cancelled.

 常言道,真是祸不单行,Timer还将它的问题传染给下一个倒霉的调用者,这个调用者原本试图提交一个TimerTask的,

 你可能希望程序会一直运行下去,然而实际情况如程序所示5秒钟后就中止了,还伴随着一个异常,

 异常的消息是"Timeralready cancelled"ScheduledThreadPoolExector妥善地处理了这个异常的任务,所以说在java5.0或更高的JDK中,几乎没有理由再使用Timer了。

 

 ScheduledThreadPoolExector改进后的例子

public class ScheduledExecutorTest {
 //线程池能按时间计划来执行任务,允许用户设定计划执行任务的时间,int类型的参数是设定  
    //线程池中线程的最小数目。当任务较多时,线程池可能会自动创建更多的工作线程来执行任务  
    public ScheduledExecutorService scheduExec = Executors.newScheduledThreadPool(1);  
    //启动计时器  
    public void lanuchTimer(){  
        Runnable task = new Runnable() {  
            public void run() {  
            System.out.println("RuntimeException ~");
                throw new RuntimeException();  
            }  
        };  
        scheduExec.scheduleWithFixedDelay(task, 1000*5, 1000*10, TimeUnit.MILLISECONDS);  
    }  
    //添加新任务  
    public void addOneTask(){  
        Runnable task = new Runnable() {  
            public void run() {  
                System.out.println("welcome to china");  
            }  
        };  
        scheduExec.scheduleWithFixedDelay(task, 1000*1, 1000, TimeUnit.MILLISECONDS);  
    }  
      
    public static void main(String[] args) throws Exception {  
        ScheduledExecutorTest test = new ScheduledExecutorTest();  
        test.lanuchTimer();  
        Thread.sleep(1000*5);//5秒钟之后添加新任务  
        test.addOneTask();  
    }  
}


打印的结果是:

RuntimeException ~
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china
welcome to china

可以看到利用线程scheduExec调度的任务在不会受到其他任务的影响。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值