我正在尝试创建一个将在每个月的同一天运行的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,我就会建议其他方法来创建月度计时器。
谢谢!
答案 0 :(得分:5)
我建议只使用Quartz并通过CronTrigger调度作业,这将允许您指定您希望在第一天的月份执行作业,并让Quartz处理时序逻辑。
Here is a further code example of how to use CronTrigger
Quartz是一个简单易用的简单库。
答案 1 :(得分:4)
如何使用计划的计时器,并在完成当前计划的任务计划时,下一步:
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更轻松地进行日期计算。
答案 2 :(得分:3)
如果您担心的是创建不需要的对象,您可以创建一个对象,该对象反过来创建/“销毁”所有引用,因此创建的对象可能是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();
}
}
答案 3 :(得分:1)
最简单的解决方案可能是使用cron或同等程序来安排独立的程序执行...
答案 4 :(得分:0)
Quartz scheduler库允许您根据cron作业表达式进行计划。
答案 5 :(得分:0)
我认为你也可以创建一个单独的线程并从DelayQueue读取来执行此操作。但它并不像ScheduledExecutorService那么容易。
答案 6 :(得分:0)
为什么每次都需要重新创建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 {}”glue code。