[Howto] Scheduling Recurring Tasks in Java Applications by fowler

반복적으로 예정되어진 작업을 하려 할 경우

 

반복적으로 예정되어진 작업(Scheduled tasks)수행하려 , 일반적으로 java.util.Timerjava.util.TimerTask이용합니다. 하지만, 맘에 들 정도로 풍부한 기능을 가지고 있진 않죠. 여기서 설명하려는 것처럼 하루에 한 번 원하는 작업을 수행하려경우엔 더더욱 그렇습니다.

여기서 설명하는 스케줄링 프레임워크는 개의 클래스와 하나의 인터페이스로 같은 작업을 아 주손쉽게 해결할 수가 있습니다. 사용하기가 아주편하며 내부를 조금만 보게 되면 변형도 손쉽네요. 그래서 소개해 드립니다.

 

한번만 실행하고 끝내버리는 스케줄링의


아래서
보이는 예제는 한번만 실행하고 멈추는 예를 보여줍니다.



import java.util.Timer;
import java.util.TimerTask;

public class EggTimer {
    private final Timer timer = new Timer();
    private final int minutes;

    public EggTimer(int minutes) {
        this.minutes = minutes;
    }

    public void start() {
        timer.schedule(new TimerTask() {
            public void run() {
                playSound();
                timer.cancel();
            }
            private void playSound() {
                System.out.println("Your egg is ready!");
                // Start a new thread to play a sound...
            }
        }, minutes * 60 * 1000);
    }

    public static void main(String[] args) {
        EggTimer eggTimer = new EggTimer(2);
        eggTimer.start();
    }

}
 




EggTimer 인스턴스가 예정된 작업을 하기 위해 Timer 인스턴스를 가지고 있고, egg Timerstart()통해 시작되면, 정해진시간이 지나고 TimerTask수행하도록 예정합니다. 시간이 되면 run() 메소드가 Timer의해 호출되죠. timercancel 되고애플리케이션도 끝납니다.

 

지금까지 것은 번만 수행하는 예이지만, Timer 인스턴스가 TimerTask  인스턴스에 대한 스케줄링을 담당하는 것을 있죠.


 

반복되는 일을 스케줄링하기

 

앞선 예제에서 보니 Timer  일정한 시간을 정해둔 뒤에 시간이 지나면 정해진 일을 수행하도록 합니다. 하지만, 대부분의 애플리케이션에서 요구하는 스케줄링은 이처럼 간단하지가 않죠. 매일 아침 500알람이 울리도록해야한다면(아침형 인간이 아니지만, 아침형 인간이라는 이모모대통령이 생각나서 꼭두새벽5시에 울린다는 가정을 해봅니다. 꼭두새벽.. 새벽5..…) 에 86400000 millisecond(24시간, 1000*60*60*24)간격을 두고 매번 울리도록 해야 하나요? 이렇게 해서 해결될 문제는 아닌듯 합니다.

 

우선 방금 들었던 얘처럼 매일  꼭두새벽에  알람이 울린다고 스케줄링을 다음과 같이 해볼 있습니다.



import java.text.SimpleDateFormat;

import java.util.Date;

import org.tiling.scheduling.Scheduler;
import org.tiling.scheduling.SchedulerTask;
import org.tiling.scheduling.examples.iterators.DailyIterator;

public class AlarmClock {

    private final Scheduler scheduler = new Scheduler();
    private final SimpleDateFormat dateFormat =
        new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS");
    private final int hourOfDay, minute, second;

    public AlarmClock(int hourOfDay, int minute, int second) {
        this.hourOfDay = hourOfDay;
        this.minute = minute;
        this.second = second;
    }

    public void start() {
        scheduler.schedule(new SchedulerTask() {
            public void run() {
                soundAlarm();
            }
            private void soundAlarm() {
                System.out.println("Wake up! " +
                    "It's " + dateFormat.format(new Date()));
                // Start a new thread to sound an alarm...
            }
        }, new DailyIterator(hourOfDay, minute, second));
    }

    public static void main(String[] args) {
        AlarmClock alarmClock = new AlarmClock(7, 0, 0);
        alarmClock.start();
    }
}
 





 

Egg Timer거의 비슷하죠?  AlarmClock 인스턴스가(Timer 인스턴스가 아닌)  Scheduler 인스턴스를 가지고 스케줄링을 하는데, 애플리케이션이 시작하면 알람을 울리기 위해(TimerTask아닌) SchedulerTask사용합니다. 다른점이 있다면, 일정시간 간격을 두고 울리도록 아니라  DailyIterator 인스턴스를 사용하네요. 500를  뜻하는 듯 합니다.

 



DailyIterator class

 

DailyIterator클래스는   ScheduleIterator 인터페이스를 구현합니다. 직접 보시죠



import java.util.Date;

public interface ScheduleIterator {
    public Date next();
}




next() 메소드는 시간순으로  Date 객체를 반복해서 생성하도록  합니다뒤에 보시겠지만, 우리가 지금의도하는  것이 하루에 한번이니 DailyIterator 클래스에서는 next()메소드가 다음 날에, 우리가 정한시각을  갖는  Date 객체를 리턴하도록 하겠네요.

(null리턴되면작업을취소하게할것이구요)

 

그럼 이제 DailyIterator 클래스를  볼까요?

 

import org.tiling.scheduling.ScheduleIterator;

import java.util.Calendar;
import java.util.Date;

/**
* A DailyIterator class returns a sequence of dates on subsequent days
* representing the same time each day.
*/
public class DailyIterator implements ScheduleIterator {
    private final int hourOfDay, minute, second;
    private final Calendar calendar = Calendar.getInstance();

    public DailyIterator(int hourOfDay, int minute, int second) {
        this(hourOfDay, minute, second, new Date());
    }

    public DailyIterator(int hourOfDay, int minute, int second, Date date) {
        this.hourOfDay = hourOfDay;
        this.minute = minute;
        this.second = second;
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, second);
        calendar.set(Calendar.MILLISECOND, 0);
        if (!calendar.getTime().before(date)) {
            calendar.add(Calendar.DATE, -1);
        }
    }

    public Date next() {
        calendar.add(Calendar.DATE, 1);
        return calendar.getTime();
    }

}





스케줄링 프레임워크를 구현하기

어떻게 사용하게 되는지를 봤고요~, 이제 프레임워크가 어떻게 구현되는지를 보도록 합시다.
ScheduleIterator와 함께 두 개의 클래스가 있습니다. Scheduler와 SchedulerTask입니다.

이 클래스들은 실제 Timer와 TimerTask클래스를 사용합니다. Scheduler/SchedulerTask 클래스의 소스를 직접 보도록 하죠

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Scheduler {

    class SchedulerTimerTask extends TimerTask {
        private SchedulerTask schedulerTask;
        private ScheduleIterator iterator;
        public SchedulerTimerTask(SchedulerTask schedulerTask,
                ScheduleIterator iterator) {
            this.schedulerTask = schedulerTask;
            this.iterator = iterator;
        }
        public void run() {
            schedulerTask.run();
            reschedule(schedulerTask, iterator);
        }
    }

    private final Timer timer = new Timer();

    public Scheduler() {
    }

    public void cancel() {
        timer.cancel();
    }

    public void schedule(SchedulerTask schedulerTask,
            ScheduleIterator iterator) {

        Date time = iterator.next();
        if (time == null) {
            schedulerTask.cancel();
        } else {
            synchronized(schedulerTask.lock) {
                if (schedulerTask.state != SchedulerTask.VIRGIN) {
                  throw new IllegalStateException("Task already
                  scheduled " + "or cancelled");
                }
                schedulerTask.state = SchedulerTask.SCHEDULED;
                schedulerTask.timerTask =
                    new SchedulerTimerTask(schedulerTask, iterator);
                timer.schedule(schedulerTask.timerTask, time);
            }
        }
    }

    private void reschedule(SchedulerTask schedulerTask,
            ScheduleIterator iterator) {

        Date time = iterator.next();
        if (time == null) {
            schedulerTask.cancel();
        } else {
            synchronized(schedulerTask.lock) {
                if (schedulerTask.state != SchedulerTask.CANCELLED) {
                    schedulerTask.timerTask =
                        new SchedulerTimerTask(schedulerTask, iterator);
                    timer.schedule(schedulerTask.timerTask, time);
                }
            }
        }
    }

}

 


SchedulerTask class

import java.util.TimerTask;

public abstract class SchedulerTask implements Runnable {

    final Object lock = new Object();

    int state = VIRGIN;
    static final int VIRGIN = 0;
    static final int SCHEDULED = 1;
    static final int CANCELLED = 2;

    TimerTask timerTask;

    protected SchedulerTask() {
    }

    public abstract void run();

    public boolean cancel() {
        synchronized(lock) {
            if (timerTask != null) {
                timerTask.cancel();
            }
            boolean result = (state == SCHEDULED);
            state = CANCELLED;
            return result;
        }
    }

    public long scheduledExecutionTime() {
        synchronized(lock) {
         return timerTask == null ? 0 : timerTask.scheduledExecutionTime();
        }
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 






















원본 출처 : http://www.ibm.com/developerworks/web/library/j-schedule.html


트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://fowler.egloos.com/tb/1489155 [도움말]
  • java.util.Timer의 schedule vs scheduleAtFixedRate 2009/07/01 11:00 #

    Java에서 일정한 시간이 지난뒤에 실행하기 Utility 클래스로 Timer 클래스가 있습니다. Timer 클래스를 사용하려다 보니, 동일한 기능의 다른 메쏘드(schedule, scheduleAtFixedRate )가 있어서 살펴보았습니다. Java API에서는 Timer의 schedule과 scheduleAtFixedRate 메쏘드 둘다, 지정한 태스크가 지정한 지연의 후에 시작되어 「고정 빈도 실행」을 반복하도록 스케줄 합니다. 그 후는...... more