Scheduled Executor Service를 사용하여 매일 특정 시간에 특정 작업을 실행하는 방법
저는 매일 새벽 5시에 특정 작업을 수행하려고 합니다. 저는 이 말을 쓰기로 .ScheduledExecutorService
하지만 지금까지 몇 분마다 작업을 실행하는 방법을 보여주는 예시를 봐왔습니다.
또한 매일 아침 특정 시간(오전 5시)에 작업을 실행하는 방법과 서머타임을 고려하는 방법을 보여주는 예를 찾을 수 없습니다.
아래는 15분마다 실행되는 코드입니다.
public class ScheduledTaskExample {
private final ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(1);
public void startScheduleTask() {
/**
* not using the taskHandle returned here, but it can be used to cancel
* the task, or check if it's done (for recurring tasks, that's not
* going to be very useful)
*/
final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(
new Runnable() {
public void run() {
try {
getDataFromDatabase();
}catch(Exception ex) {
ex.printStackTrace(); //or loggger would be better
}
}
}, 0, 15, TimeUnit.MINUTES);
}
private void getDataFromDatabase() {
System.out.println("getting data...");
}
public static void main(String[] args) {
ScheduledTaskExample ste = new ScheduledTaskExample();
ste.startScheduleTask();
}
}
할 수 요?ScheduledExecutorService
서타는는는는는는는면면면면면면면면?
★★★★★★★★★★★★★★★★.TimerTask
or is is is is is is isScheduledExecutorService
8로 Java SE 8과 함께 날짜 를 java.time
은 보다 할 수.java.util.Calendar
★★★★★★★★★★★★★★★★★」java.util.Date
.
- 날짜 시간 클래스의 예를 사용합니다. 이 새 API의 LocalDateTime
- ZonedDateTime 클래스를 사용하여 일광 절약 시간제 문제를 포함한 시간대별 계산을 처리합니다.여기에 튜토리얼과 예가 있습니다.
다음으로 사용 사례를 사용하여 작업을 스케줄링하는 예를 제시하겠습니다.
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
nextRun = nextRun.plusDays(1);
Duration duration = Duration.between(now, nextRun);
long initialDelay = duration.getSeconds();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(),
initialDelay,
TimeUnit.DAYS.toSeconds(1),
TimeUnit.SECONDS);
initialDelay
에게 ""을 됩니다.TimeUnit.SECONDS
이 사용 예에서는 단위 밀리초 이하의 시차 문제는 무시할 수 있을 것 같습니다. 쓸 수 요.duration.toMillis()
★★★★★★★★★★★★★★★★★」TimeUnit.MILLISECONDS
스케줄링 계산을 밀리초 단위로 처리합니다.
또한 TimerTask가 이 서비스 또는 ScheduledExecutorService에 더 적합합니까?
니니: ScheduledExecutorService
TimerTask
StackOverflow는 이미 답변을 가지고 있습니다.
@PaddyD에서
적절한 현지 시간에 실행하려면 이 프로그램을 1년에 두 번 다시 시작해야 하는 문제가 여전히 남아 있습니다.scheduleAtFixedRate는 1년 내내 같은 UTC 시간이 만족스럽지 않으면 잘라내지 않습니다.
1)을했으므로, @PaddyD에서 합니다.ScheduledExecutorService
데몬 스레드를 사용하는 것은 위험합니다.
class MyTaskExecutor
{
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
MyTask myTask;
volatile boolean isStopIssued;
public MyTaskExecutor(MyTask myTask$)
{
myTask = myTask$;
}
public void startExecutionAt(int targetHour, int targetMin, int targetSec)
{
Runnable taskWrapper = new Runnable(){
@Override
public void run()
{
myTask.execute();
startExecutionAt(targetHour, targetMin, targetSec);
}
};
long delay = computeNextDelay(targetHour, targetMin, targetSec);
executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
}
private long computeNextDelay(int targetHour, int targetMin, int targetSec)
{
LocalDateTime localNow = LocalDateTime.now();
ZoneId currentZone = ZoneId.systemDefault();
ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
if(zonedNow.compareTo(zonedNextTarget) > 0)
zonedNextTarget = zonedNextTarget.plusDays(1);
Duration duration = Duration.between(zonedNow, zonedNextTarget);
return duration.getSeconds();
}
public void stop()
{
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException ex) {
Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
주의:
MyTask
는, 「기능이 있는 입니다.execute
.- " " 중
ScheduledExecutorService
, , 을 사용합니다awaitTermination
를 한 후shutdown
태스크가 정체되거나 교착 상태가 되어 사용자가 영원히 기다릴 가능성이 항상 있습니다.
이전에 Calender와 함께 제시한 예는 단지 제가 언급한 아이디어일 뿐입니다.정확한 시간 계산과 일광절약 문제는 피했습니다.@PaddyD의 클레임에 따라 솔루션을 업데이트.
Java 8의 경우:
scheduler = Executors.newScheduledThreadPool(1);
//Change here for the hour you want ----------------------------------.at()
Long midnight=LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES);
scheduler.scheduleAtFixedRate(this, midnight, 1440, TimeUnit.MINUTES);
Java 8을 사용할 수 없는 경우 다음과 같은 작업을 수행할 수 있습니다.
public class DailyRunnerDaemon
{
private final Runnable dailyTask;
private final int hour;
private final int minute;
private final int second;
private final String runThreadName;
public DailyRunnerDaemon(Calendar timeOfDay, Runnable dailyTask, String runThreadName)
{
this.dailyTask = dailyTask;
this.hour = timeOfDay.get(Calendar.HOUR_OF_DAY);
this.minute = timeOfDay.get(Calendar.MINUTE);
this.second = timeOfDay.get(Calendar.SECOND);
this.runThreadName = runThreadName;
}
public void start()
{
startTimer();
}
private void startTimer();
{
new Timer(runThreadName, true).schedule(new TimerTask()
{
@Override
public void run()
{
dailyTask.run();
startTimer();
}
}, getNextRunTime());
}
private Date getNextRunTime()
{
Calendar startTime = Calendar.getInstance();
Calendar now = Calendar.getInstance();
startTime.set(Calendar.HOUR_OF_DAY, hour);
startTime.set(Calendar.MINUTE, minute);
startTime.set(Calendar.SECOND, second);
startTime.set(Calendar.MILLISECOND, 0);
if(startTime.before(now) || startTime.equals(now))
{
startTime.add(Calendar.DATE, 1);
}
return startTime.getTime();
}
}
외부 리브가 필요 없고 여름 시간대를 감안합니다. 중 .Calendar
및 는 '''로 지정됩니다.Runnable
§:
Calendar timeOfDay = Calendar.getInstance();
timeOfDay.set(Calendar.HOUR_OF_DAY, 5);
timeOfDay.set(Calendar.MINUTE, 0);
timeOfDay.set(Calendar.SECOND, 0);
new DailyRunnerDaemon(timeOfDay, new Runnable()
{
@Override
public void run()
{
try
{
// call whatever your daily task is here
doHousekeeping();
}
catch(Exception e)
{
logger.error("An error occurred performing daily housekeeping", e);
}
}
}, "daily-housekeeping");
N.B. 타이머 태스크는 IO 수행에 권장되지 않는 데몬 스레드에서 실행됩니다.사용자 스레드를 사용해야 할 경우 타이머를 취소하는 다른 방법을 추가해야 합니다.
를 해야 하는 ScheduledExecutorService
「」를 해 주세요.startTimer
지정합니다.
private void startTimer()
{
Executors.newSingleThreadExecutor().schedule(new Runnable()
{
Thread.currentThread().setName(runThreadName);
dailyTask.run();
startTimer();
}, getNextRunTime().getTime() - System.currentTimeMillis(),
TimeUnit.MILLISECONDS);
}
하지 않지만, 「」를 해, 「스톱 메서드」가 할 수 .shutdownNow
ScheduledExecutorService
그렇지 않으면 정지하려고 할 때 응용 프로그램이 중단될 수 있습니다.
Java8:
」 。
- 유휴 스레드가 있는 스레드 풀 때문에 웹 응용 프로그램 서버가 중지하지 않을 때 수정됨
- 재귀 없음
- 커스텀 현지 시간으로 작업을 실행합니다(내 경우 벨로루시, 민스크).
/**
* Execute {@link AppWork} once per day.
* <p>
* Created by aalexeenka on 29.12.2016.
*/
public class OncePerDayAppWorkExecutor {
private static final Logger LOG = AppLoggerFactory.getScheduleLog(OncePerDayAppWorkExecutor.class);
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private final String name;
private final AppWork appWork;
private final int targetHour;
private final int targetMin;
private final int targetSec;
private volatile boolean isBusy = false;
private volatile ScheduledFuture<?> scheduledTask = null;
private AtomicInteger completedTasks = new AtomicInteger(0);
public OncePerDayAppWorkExecutor(
String name,
AppWork appWork,
int targetHour,
int targetMin,
int targetSec
) {
this.name = "Executor [" + name + "]";
this.appWork = appWork;
this.targetHour = targetHour;
this.targetMin = targetMin;
this.targetSec = targetSec;
}
public void start() {
scheduleNextTask(doTaskWork());
}
private Runnable doTaskWork() {
return () -> {
LOG.info(name + " [" + completedTasks.get() + "] start: " + minskDateTime());
try {
isBusy = true;
appWork.doWork();
LOG.info(name + " finish work in " + minskDateTime());
} catch (Exception ex) {
LOG.error(name + " throw exception in " + minskDateTime(), ex);
} finally {
isBusy = false;
}
scheduleNextTask(doTaskWork());
LOG.info(name + " [" + completedTasks.get() + "] finish: " + minskDateTime());
LOG.info(name + " completed tasks: " + completedTasks.incrementAndGet());
};
}
private void scheduleNextTask(Runnable task) {
LOG.info(name + " make schedule in " + minskDateTime());
long delay = computeNextDelay(targetHour, targetMin, targetSec);
LOG.info(name + " has delay in " + delay);
scheduledTask = executorService.schedule(task, delay, TimeUnit.SECONDS);
}
private static long computeNextDelay(int targetHour, int targetMin, int targetSec) {
ZonedDateTime zonedNow = minskDateTime();
ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec).withNano(0);
if (zonedNow.compareTo(zonedNextTarget) > 0) {
zonedNextTarget = zonedNextTarget.plusDays(1);
}
Duration duration = Duration.between(zonedNow, zonedNextTarget);
return duration.getSeconds();
}
public static ZonedDateTime minskDateTime() {
return ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
}
public void stop() {
LOG.info(name + " is stopping.");
if (scheduledTask != null) {
scheduledTask.cancel(false);
}
executorService.shutdown();
LOG.info(name + " stopped.");
try {
LOG.info(name + " awaitTermination, start: isBusy [ " + isBusy + "]");
// wait one minute to termination if busy
if (isBusy) {
executorService.awaitTermination(1, TimeUnit.MINUTES);
}
} catch (InterruptedException ex) {
LOG.error(name + " awaitTermination exception", ex);
} finally {
LOG.info(name + " awaitTermination, finish");
}
}
}
Quartz Scheduler 같은 것을 사용해 본 적이 있습니까?이 라이브러리에는 cron like 식을 사용하여 매일 정해진 시간에 작업을 실행하도록 스케줄링하는 메커니즘이 있습니다( 참조).CronScheduleBuilder
를 참조해 주세요.
코드 예시(테스트되지 않음):
public class GetDatabaseJob implements InterruptableJob
{
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
getFromDatabase();
}
}
public class Example
{
public static void main(String[] args)
{
JobDetails job = JobBuilder.newJob(GetDatabaseJob.class);
// Schedule to run at 5 AM every day
ScheduleBuilder scheduleBuilder =
CronScheduleBuilder.cronSchedule("0 0 5 * * ?");
Trigger trigger = TriggerBuilder.newTrigger().
withSchedule(scheduleBuilder).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
}
미리 해야 할 일이 조금 더 있고 작업 실행 코드를 다시 작성해야 할 수도 있지만, 작업을 실행하는 방법을 더 잘 제어할 수 있어야 합니다.또한 필요하다면 일정을 변경하는 것이 더 쉬울 것입니다.
저도 비슷한 문제가 있었어요. 동안 할 했다.ScheduledExecutorService
이 문제는 오전 3시 30분에 시작하는 하나의 작업으로 해결되었습니다. 다른 모든 작업은 현재 시간에 비례하여 스케줄이 정해졌습니다.다음 날 새벽 3시 30분으로 일정을 조정했습니다.
이 시나리오에서는 서머타임이 더 이상 문제가 되지 않습니다.
간단한 날짜 해석을 사용할 수 있습니다.하루 중 시간이 지금 이전이라면 내일부터 시작합시다.
String timeToStart = "12:17:30";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss");
SimpleDateFormat formatOnlyDay = new SimpleDateFormat("yyyy-MM-dd");
Date now = new Date();
Date dateToStart = format.parse(formatOnlyDay.format(now) + " at " + timeToStart);
long diff = dateToStart.getTime() - now.getTime();
if (diff < 0) {
// tomorrow
Date tomorrow = new Date();
Calendar c = Calendar.getInstance();
c.setTime(tomorrow);
c.add(Calendar.DATE, 1);
tomorrow = c.getTime();
dateToStart = format.parse(formatOnlyDay.format(tomorrow) + " at " + timeToStart);
diff = dateToStart.getTime() - now.getTime();
}
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(), TimeUnit.MILLISECONDS.toSeconds(diff) ,
24*60*60, TimeUnit.SECONDS);
서버가 4:59에 다운되면 어떻게 됩니까?오전 5시 1분에 다시 오나요?그냥 도망칠 것 같아.일정 데이터를 어딘가에 저장할 수 있는 Quartz와 같은 영구 스케줄러를 추천합니다.그런 다음 이 실행이 아직 수행되지 않은 것을 확인하고 5:01에 실행합니다.오전.
빅터의 대답을 합산하기 위해서요
긴 )가 있는지합니다.midnight
요.1440
만약 그렇다면, 나는 그 일을 생략할 것이다..plusDays(1)
그렇지 않으면 작업은 모레만 실행됩니다.
간단하게 이렇게 했어요.
Long time;
final Long tempTime = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(7, 0), ChronoUnit.MINUTES);
if (tempTime > 1440) {
time = LocalDateTime.now().until(LocalDate.now().atTime(7, 0), ChronoUnit.MINUTES);
} else {
time = tempTime;
}
다음 예는 나에게 도움이 됩니다.
public class DemoScheduler {
public static void main(String[] args) {
// Create a calendar instance
Calendar calendar = Calendar.getInstance();
// Set time of execution. Here, we have to run every day 4:20 PM; so,
// setting all parameters.
calendar.set(Calendar.HOUR, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.AM_PM, Calendar.AM);
Long currentTime = new Date().getTime();
// Check if current time is greater than our calendar's time. If So,
// then change date to one day plus. As the time already pass for
// execution.
if (calendar.getTime().getTime() < currentTime) {
calendar.add(Calendar.DATE, 1);
}
// Calendar is scheduled for future; so, it's time is higher than
// current time.
long startScheduler = calendar.getTime().getTime() - currentTime;
// Setting stop scheduler at 4:21 PM. Over here, we are using current
// calendar's object; so, date and AM_PM is not needed to set
calendar.set(Calendar.HOUR, 5);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.AM_PM, Calendar.PM);
// Calculation stop scheduler
long stopScheduler = calendar.getTime().getTime() - currentTime;
// Executor is Runnable. The code which you want to run periodically.
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("test");
}
};
// Get an instance of scheduler
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// execute scheduler at fixed time.
scheduler.scheduleAtFixedRate(task, startScheduler, stopScheduler, MILLISECONDS);
}
}
레퍼런스: https://chynten.wordpress.com/2016/06/03/java-scheduler-to-run-every-day-on-specific-time/
아래 클래스를 사용하여 매일 특정 시간에 작업을 예약할 수 있습니다.
package interfaces;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class CronDemo implements Runnable{
public static void main(String[] args) {
Long delayTime;
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final Long initialDelay = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(12, 30), ChronoUnit.MINUTES);
if (initialDelay > TimeUnit.DAYS.toMinutes(1)) {
delayTime = LocalDateTime.now().until(LocalDate.now().atTime(12, 30), ChronoUnit.MINUTES);
} else {
delayTime = initialDelay;
}
scheduler.scheduleAtFixedRate(new CronDemo(), delayTime, TimeUnit.DAYS.toMinutes(1), TimeUnit.MINUTES);
}
@Override
public void run() {
System.out.println("I am your job executin at:" + new Date());
}
}
이렇게 쓸 수 있는데 왜 상황을 복잡하게 만들죠? (네, > 낮은 응집력, 하드코드화 - > 하지만 이것은 하나의 예이며 유감스럽게도 필수적인 방법입니다.)상세한 것에 대하여는, 아래의 코드 예를 참조해 주세요).
package timer.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.*;
public class TestKitTimerWithExecuterService {
private static final Logger log = LoggerFactory.getLogger(TestKitTimerWithExecuterService.class);
private static final ScheduledExecutorService executorService
= Executors.newSingleThreadScheduledExecutor();// equal to => newScheduledThreadPool(1)/ Executor service with one Thread
private static ScheduledFuture<?> future; // why? because scheduleAtFixedRate will return you it and you can act how you like ;)
public static void main(String args[]){
log.info("main thread start");
Runnable task = () -> log.info("******** Task running ********");
LocalDateTime now = LocalDateTime.now();
LocalDateTime whenToStart = LocalDate.now().atTime(20, 11); // hour, minute
Duration duration = Duration.between(now, whenToStart);
log.info("WhenToStart : {}, Now : {}, Duration/difference in second : {}",whenToStart, now, duration.getSeconds());
future = executorService.scheduleAtFixedRate(task
, duration.getSeconds() // difference in second - when to start a job
,2 // period
, TimeUnit.SECONDS);
try {
TimeUnit.MINUTES.sleep(2); // DanDig imitation of reality
cancelExecutor(); // after canceling Executor it will never run your job again
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("main thread end");
}
public static void cancelExecutor(){
future.cancel(true);
executorService.shutdown();
log.info("Executor service goes to shut down");
}
}
여기서는 코드가 단일 VM에서 실행되고 있다고 가정합니다.코드가 여러 VM에서 실행 중인 경우 이 스케줄러 코드가 여러 번 실행됩니다.그건 따로 처리해야 해요.
언급URL : https://stackoverflow.com/questions/20387881/how-to-run-certain-task-every-day-at-a-particular-time-using-scheduledexecutorse
'programing' 카테고리의 다른 글
고정 크기 해시 맵에 대한 최적의 용량과 부하 계수는 얼마입니까? (0) | 2022.10.07 |
---|---|
Java 8: Java.util.function의 TriFunction(및 kin)은 어디에 있습니까?아니면 대체방법이 뭐죠? (0) | 2022.10.07 |
MySQL에 임의 날짜 삽입/업데이트 (0) | 2022.10.06 |
스프링 부트 시작 시간 단축 (0) | 2022.10.06 |
4자리 이상의 고유 숫자로 구성된 번호를 일치시키는 방법 (0) | 2022.10.06 |