Ежемесячный таймер Java - PullRequest
       5

Ежемесячный таймер Java

3 голосов
/ 05 ноября 2010

Я пытаюсь создать 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 создает второй таймер, будет ли сохраняться первый таймер, потому что он создал второй? После завершения кода на первом таймере будет ли этот поток и объект обрабатываться сборщиком мусора? Со временем я не хочу создавать группу потоков, которые ничего не делают, но не удаляются. Может быть, я не совсем понимаю, как работают потоки и таймеры ...

Я открыт для предложений о других способах создания месячного таймера, если мне не нужно использовать сторонние JAR-файлы.

Спасибо!

Ответы [ 7 ]

5 голосов
/ 05 ноября 2010

Я бы предложил просто использовать Кварц и планировать задания с помощью CronTrigger , который позволит вам указать, хотите ли вы выполнять задание в первый день месяца, и пусть Кварц обрабатывает логику синхронизации.

Вот еще один пример кода использования CronTrigger .

Quartz - очень простая в использовании библиотека.

4 голосов
/ 05 ноября 2010

Как насчет просто использования запланированного таймера, и по мере того, как вы выполняете текущее расписание запланированных задач, следующее:

 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 , чтобы упростить вычисления даты.

3 голосов
/ 05 ноября 2010

Если вас беспокоит создание ненужных объектов, вы всегда можете создать объект, который, в свою очередь, создает / «уничтожает» все ссылки, поэтому созданные объекты могут быть скопированы.

В худшем случае у вас будет 12 ненужных объектов в год, что, я думаю, терпимо. Тем не менее ваша забота действительна.

Вот моя попытка следовать предложению Джоэла о расписании в конце исполнения. Обратите внимание, что текущий таймер заменен новым, так что и таймер, и задача таймера могут быть сброшены.

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();

    }
}
1 голос
/ 05 ноября 2010

простейшим решением может быть использование cron или его эквивалента для планирования выполнения автономной программы ...

0 голосов
/ 04 июля 2012

Зачем вам каждый раз воссоздавать таймер? Таймер - это просто нить с клейким кодом вокруг. Отмена этого вызывает завершение других задач, работающих на этом таймере.

Лучше использовать следующее:

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 {}".

0 голосов
/ 05 ноября 2010

Я думаю, вы могли бы также создать один отдельный поток и прочитать из DelayQueue , чтобы сделать это.Но это не так просто, как ScheduledExecutorService.

0 голосов
/ 05 ноября 2010

Библиотека Quartz позволяет планировать на основе выражений заданий cron.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...