替代java

时间:2016-11-15 13:08:33

标签: java multithreading calendar thread-sleep

我在我的应用程序中使用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()可能会导致性能问题。在我的情况下,还有其他方法可以提高性能吗?

最初我们使用ThreadPoolTask​​Executor安排了近50项服务。一些服务没有正常启动。我们无法找到过去1个月的确切问题。所以我们想尝试执行者

请参阅此Spring scheduler is stopping unexpectedly and starting again

4 个答案:

答案 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)

使用ScheduledExecutorService

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或任何您想要的)。但请记住,如果你的工作人员在不同的时间框架内触发,你可以重复使用较少量的线程,特别是如果它们不能长时间运行。

此外,请确保拦截工作人员中发生的所有异常,以避免您观察到的意外故障。

有两种常见的模式可以实现这一目标:

  1. try / catch块中的Surround worker代码:

    public void run() {
        try {
            doRun();
        } catch (Throwable t) {
            LOG.error("exception occurred in worker", t);
        }
    }
    
  2. 使用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);
            }
        }
    }