반복적으로 예정되어진 작업을 하려 할 경우
반복적으로 예정되어진 작업(Scheduled tasks)을수행하려 할 때, 일반적으로 java.util.Timer와 java.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();
}
}
지금까지 본 것은 한 번만 수행하는 예이지만, Timer 인스턴스가 TimerTask 인스턴스에 대한 스케줄링을 담당하는 것을 볼 수 있죠.
반복되는 일을 스케줄링하기
앞선 예제에서 보니 Timer는 일정한 시간을 정해둔 뒤에 그 시간이 지나면 정해진 일을 수행하도록 합니다. 하지만, 대부분의 애플리케이션에서 요구하는 스케줄링은 이처럼 간단하지가 않죠. 매일
우선 방금 들었던 얘처럼 매일 꼭두새벽에 알람이 울린다고 할 때 스케줄링을 다음과 같이 해볼 수 있습니다.
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 인스턴스를 사용하네요.
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
