2016-11-15 1138 views
1

我在我的应用程序中使用了Spring ThreadPoolTask​​Executor。我的方法的一个方法需要仅在周五午夜之前调用。该方法被调用服务器startup.So我现在用的是Thread.sleep()以下列方式Java中的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 month.So确切的问题,我们希望与执行尝试

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

+0

对于需要在特定日期的特定时间定期运行的程序,您应该使用quartz scheduler或cron。 – RealSkeptic

+0

“提高性能”?如果你睡了一天,表现真的不是问题。您遇到的更大问题是确保您的JVM仍在运行。 –

+0

我会亲自使用cronjob,调度程序,ExecutorService或类似的东西。因为Thread.sleep一周看起来很浪费。 –

回答

1

你在你的评论说你正在使用弹簧。所以你可以使用cron表达式。有关如何使用Scheduler的更多详细信息,请参阅34. Task Execution and Scheduling

@Scheduled(cron="0 0 1 ? * FRI *") 
public void doSomething() { 
    // do stuff 
} 

这将使它每周在星期五凌晨1点运行。您可以使用Cron Maker来生成表达式。

在您的配置类中,您必须启用调度。

@Configuration 
@EnableAsync 
@EnableScheduling 
public class AppConfig { 
} 
+0

感谢Murat.But有没有使用调度的可能性,即执行者 – PSR

+0

@PSR为什么你要避免使用调度程序?它属于同一个框架,并且它是该框架中用于在给定日期和时间运行任务的正确工具。 – RealSkeptic

+0

@PSR避免调度程序是没有意义的,因为它会像你想做的那样完成。 –

0

您可以使用ScheduledExecutorService安排工作在一段时间后进行。

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); 
     scheduler.schedule(() -> { 
      /* 
      * define work to be done inside this lambda 
      */ 
     }, diffTime, TimeUnit.MILLISECONDS); 

注意:如果你希望发生在每周五的午夜(因此不只是本周五)的工作,你可以修改日程安排命令在设定的时间间隔发生:

scheduler.scheduleAtFixedRate(() -> { 
    /* 
    * define work to be done inside this lambda 
    */ 
    }, diffTime, Duration.ofDays(7).toMillis(), TimeUnit.MILLISECONDS); 
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重新安排下一次执行的任务。

0

根据其他用户的建议使用ThreadPoolTaskScheduler,它由Spring框架提供,并位于与ThreadPoolTaskExecutor相同的包中。

另一种替代方案是ScheduledExecutorService,它由Core Java提供并具有类似的功能。

这两个服务允许您配置线程池大小(500或任何你想要的)。但请记住,如果您的员工在不同的时间段触发,您可以重复使用较少量的线程,尤其是在长时间不运行的情况下。

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

有两种常见的方式来实现:

  1. 在try/catch块

    环绕工人代码:

    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); 
         } 
        } 
    }