Java月计时器

我正在尝试创建一个Timer / TimerTask,它将在每个月的同一天运行。 我无法安排重复计时器,因为一个月不会总是相同的时间长度。

所以,这是我的解决方案:

public class MyTask extends TimerTask { public void run(){ //do process file stuff if(scheduledExecutionTime() != 0){ TimerHelper.restartMyTimer(); } } } public class TimerHelper { public static HashTable timersTable = new HashTable(); public static void restartMyTimer(){ Calendar runDate = Calendar.getInstance(); runDate.set(Calendar.DAY_OF_MONTH, 1); runDate.set(Calendar.HOUR_OF_DAY, 4); runDate.set(Calendar.MINUTE, 0); runDate.add(Calendar.MONTH, 1);//set to next month MyTask myTask = new MyTask(); Timer myTimer = new Timer(); myTimer.schedule(myTask, runDate.getTime()); timersTable = new HashTable();//keeping a reference to the timer so we timersTable.put("1", myTimer);//have the option to cancel it later } } 

我认为我将遇到的问题是因为第一个TimerTask创建了第二个Timer,第一个Timer会被保留,因为它创建了第二个吗? 代码在第一个Timer完成后,是否会通过垃圾回收来处理该线程和对象? 随着时间的推移,我不想建立一堆没有做任何事情但没有被删除的线程。 也许我对Threads和Timers的运作方式没有正确的理解……

只要我不必使用第三方JAR,我就可以建议其他方法来创建月度计时器。

谢谢!

如果您担心的是创建不需要的对象,您可以创建一个对象,该对象反过来创建/“销毁”所有引用,因此创建的对象可能是gc’ed。

在最糟糕的情况下,一年内你将有12个不需要的物品,我认为这是可以忍受的。 您的担忧仍然有效。

这是我在执行结束后跟随Joel建议的计划。 注意,当前的Timer被一个新的替换,因此,定时器和定时器任务都可以是gc’ed。

 package monthly.schedule; import java.util.Timer; import java.util.TimerTask; import java.util.Date; import java.util.Calendar; public class MonthlyTimer { // What to do private final Runnable whatToDo; // when private final int dayOfMonth; private final int hourOfDay; // The current timer private Timer current = new Timer();//to avoid NPE public void cancelCurrent() { current.cancel();// cancel this execution; current.purge(); // removes the timertask so it can be gc'ed } // create a new instance public static MonthlyTimer schedule( Runnable runnable, int dayOfMonth, int hourOfDay ) { return new MonthlyTimer( runnable, dayOfMonth, hourOfDay ); } private MonthlyTimer(Runnable runnable, int day, int hour ) { this.whatToDo = runnable; this.dayOfMonth = day; this.hourOfDay = hour; schedule(); } // Schedules the task for execution on next month. private void schedule() { // Do you mean like this? cancelCurrent(); current = new Timer(); // assigning a new instance // will allow the previous Timer to be gc'ed current.schedule( new TimerTask() { public void run() { try { whatToDo.run(); } finally { schedule();// schedule for the next month } } } , nextDate() ); } // Do the next date stuff private Date nextDate() { Calendar runDate = Calendar.getInstance(); runDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); runDate.set(Calendar.HOUR_OF_DAY, hourOfDay); runDate.set(Calendar.MINUTE, 0); runDate.add(Calendar.MONTH, 1);//set to next month return runDate.getTime(); } } class UseIt { public static void main( String [] args ) { int the1st = 1; int at16hrs = 16; MonthlyTimer t = MonthlyTimer.schedule( new Runnable() { public void run() { System.out.println( "Hola" ); }}, the1st, at16hrs ); // will print "Hola" every 1st at 16:00 hrs. // if needed you can cancel with: t.cancelCurrent(); } } 

我建议只使用Quartz并通过CronTrigger调度作业,这将允许您指定您希望在第一天的月份执行作业,并让Quartz处理时序逻辑。

以下是如何使用CronTrigger的进一步代码示例 。

Quartz是一个简单易用的简单库。

如何使用计划的计时器,并在完成当前计划的任务计划时:

  ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); es.schedule(new MyTask(), numberOfDaysRemaining(), TimeUnit.DAYS); class MyTask implements Runnable { public void run() { try { // do it } finally { es.schedule(new MyTask(), numberOfDaysRemaining(), TimeUnit.DAYS); } } } 

您可以使用JodaTime更轻松地进行日期计算。

最简单的解决方案可能是使用cron或同等程序来安排一个独立的程序执行…

Quartz调度程序库允许您根据cron作业表达式进行调度。

我想你也可以创建一个单独的线程并从DelayQueue读取来做到这一点。 但它并不像ScheduledExecutorService那么容易。

为什么每次都需要重新创建Timer? Timer只是带有胶水代码的线程。 取消它会导致终止在该Timer上运行的其他任务。

最好使用以下内容:

 MonthlyTimer extends Timer { public void execute(TimerTask task, Date date, int dayOfMonth) { this.schedule(new TimerTaskWithCallback(task, dayOfMonth, this), date); } void taskCallback(TimerTaskWithCallback task) { this.schedule(new TimerTaskWithCallback(task.getImpl()), nextDate(task.getDayOfMonth())); //next date could be used from Oscar's post. } } 

TimerTaskWithCallback只在原始任务执行后执行MonthlyTimer.taskCallback。 可以“try {} catch {} finally {}”胶水代码。