我在我的应用程序中使用spring ThreadPoolTaskExecutor
。我的一种方法只需要在周五午夜召唤。在服务器启动时调用该方法。所以我按照以下方式使用Thread.sleep()
Calendar today = Calendar.getInstance();
int dayOfWeek = today.get(Calendar.DAY_OF_WEEK);
int daysUntilNextFriday = Calendar.FRIDAY - dayOfWeek;
if(daysUntilNextFriday < 0){
daysUntilNextFriday = daysUntilNextFriday + 7;
}
Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_YEAR, daysUntilNextFriday);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
Date d1 = c.getTime();
long diffTime = d1.getTime() - new Date().getTime();
Thread.sleep(diffTime);
在某些情况下,它也可能会等待超过1天。有没有更好的方法,而不是使用Thread.sleep()
。我在某些地方看到使用Thread.sleep()
可能会导致性能问题。在我的情况下,还有其他方法可以提高性能吗?
最初我们使用ThreadPoolTaskExecutor安排了近50项服务。一些服务没有正常启动。我们无法找到过去1个月的确切问题。所以我们想尝试执行者
请参阅此Spring scheduler is stopping unexpectedly and starting again
答案 0 :(得分:1)
你在评论中说过你正在使用Spring。所以你可以使用cron表达式。有关如何使用Scheduler
的详细信息,请参阅34. Task Execution and Scheduling。
@Scheduled(cron="0 0 1 ? * FRI *")
public void doSomething() {
// do stuff
}
这将使其在周五凌晨1点运行。您可以使用Cron Maker生成表达式。
在Configuration类中,您必须通过。
启用调度@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
答案 1 :(得分:1)
您可以使用ScheduledExecutorService来安排在一定时间后进行的工作。
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.schedule(() -> {
/*
* define work to be done inside this lambda
*/
}, diffTime, TimeUnit.MILLISECONDS);
注意:如果您希望工作在每个星期五午夜进行(因此不仅仅是这个星期五),您可以修改schedule命令以设定的间隔发生:
scheduler.scheduleAtFixedRate(() -> {
/*
* define work to be done inside this lambda
*/
}, diffTime, Duration.ofDays(7).toMillis(), TimeUnit.MILLISECONDS);
答案 2 :(得分:0)
private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
如果你不担心准确性(就像照顾夏令时一样):
您可以估算一个固定的周期:
7 * 24 * 60 * 60 * 1000 = 604800000(以毫秒为单位)
然后你可以像这样调用执行程序:
Runnable task = <your task extending Runnable>
long difftime = <time to next friday>;
long week = 7 * 24 * 60 * 60 * 1000;
executor.scheduleAtFixedRate(task, difftime, week, TimeUnit.MILLISECONDS);
这将导致执行程序首先等待difftime-delay,然后以指定的周期执行任务。
如果 担心准确性: 然后拨打电话:
executor.schedule(task, difftime, TimeUnit.MILLISECONDS);
只执行一次任务。在您的任务中 - 调用一个方法,该方法将使用新计算的difftime为下次执行重新安排任务。
答案 3 :(得分:0)
正如其他用户所建议的那样,使用ThreadPoolTaskScheduler
,它由Spring框架提供,并且位于同一个包ThreadPoolTaskExecutor
中。
另一个替代方案是ScheduledExecutorService
,它由Core Java提供并具有类似的功能。
这两种服务都允许您配置线程池大小(500或任何您想要的)。但请记住,如果你的工作人员在不同的时间框架内触发,你可以重复使用较少量的线程,特别是如果它们不能长时间运行。
此外,请确保拦截工作人员中发生的所有异常,以避免您观察到的意外故障。
有两种常见的模式可以实现这一目标:
try / catch块中的Surround worker代码:
public void run() {
try {
doRun();
} catch (Throwable t) {
LOG.error("exception occurred in worker", t);
}
}
使用UncaughtExceptionHandler
拦截并记录未拦截异常的所有线程中的失败:
public class DefaultUncaughtExceptionHandler implements UncaughtExceptionHandler {
static {
if (Thread.getDefaultUncaughtExceptionHandler() == null) {
Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler());
}
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!(e instanceof ThreadDeath)) {
LOG.error("uncaught exception occurred in thread " + t.getName(), e);
}
}
}