【问题标题】:Run Java code once every hour每小时运行一次 Java 代码
【发布时间】:2012-10-03 17:08:59
【问题描述】:

我正在尝试编写一个简单的 Java 程序,该程序在分针为 20 时每小时运行一些代码。问题是我这样做的方式非常耗费 CPU。我熟悉 Quartz,但我正在寻找一个更简单的解决方案,有什么想法吗?

    boolean run = true;
    while(run){
        Calendar calendar = new GregorianCalendar();
        int minute = calendar.get(Calendar.MINUTE);
        if(minute == 20){
            //Do some Stuff             
        }
    }

【问题讨论】:

  • 你在什么系统上?在 linux 上,你可能会安排一个 cron 作业。
  • 更新:可怕的 Calandar 已被 JSR 310 中定义的 java.time 类所取代。

标签: java multithreading jakarta-ee scheduled-tasks


【解决方案1】:

一个简单的解决方案是使用 Executors 框架:

final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.scheduleAtFixedRate(task, secondsToFirstOccurence, 60*60, TimeUnit.SECONDS);

并使用一些逻辑找出secondsToFirstOccurence。这可能涉及Calendar 实例,但使用 JodaTime 会更方便。

【讨论】:

  • 这种替代方案在托管环境中是否会很好地发挥作用?它会同步阻塞当前线程吗?如果任何一个问题的答案都是“是”,我建议不要这样做,更喜欢容器外部的异步服务——比如 Quartz。
  • @ÓscarLópez 引用 OP:“我正在尝试编写一个简单的 Java 程序”。请根据具体情况调整您的建议。
  • 我问这个问题是因为这个问题被标记为 java-ee ,所以我假设“简单应用程序”存在于 JEE 容器中——毕竟它并不那么简单。
  • @ÓscarLópez 它也被标记为“eclipse”和“algorithm”,所以去看看。我在谷歌上搜索了 Quartz,第二次点击是 this page:“为什么你不应该使用 Quartz 调度程序”。
【解决方案2】:

请注意,如果您的应用程序在托管环境(Web 或 ejb 容器)中运行,则不允许使用 Thread.sleep() 或任何其他与线程相关的操作,就此而言,请查看 @ 987654321@ 页。我对此提出警告,因为该问题被标记为 java-ee,因此“简单应用程序”可能并不那么简单——如果它在 Java EE 容器中运行,则需要注意其他事项。

如果您正在构建企业级应用程序,请忘记Thread.sleep()。找一个成熟的作业调度器,使用Quartz,它是一个开源且非常成熟和可靠的产品。或者使用Obsidian Scheduler,这是一个功能丰富的商业调度程序,比 Quartz 具有更多开箱即用的功能。

完全成熟的调度程序(但适合在容器内运行)的轻量级替代方案是使用Timer service

【讨论】:

  • 第二段对我来说是新闻。愿意提供消息来源吗?
  • 这涵盖了 EJB 的情况,但这适用于任何 Web 容器吗?例如,Servlet 规范谈到“应用程序创建的线程”(Servlet 3.0 规范,第 104 页)。
  • 在托管环境中不应以任何方式创建、销毁或操作线程。即使你可以,你不应该 - 这样做会破坏容器控制资源的能力
  • @meriton 只需考虑如何使应用程序取消部署可预见地安全。
  • @meriton 我们在谈论同样的事情。我并不是说您不允许或不应该在 servlet 中创建线程。我在强调可能的后果,这解释了为什么其他人可能会声称您不应该这样做。
【解决方案3】:

您可能在两次通话之间寻找Thread.sleep()

【讨论】:

    【解决方案4】:

    查看java.util.Timer方法 scheduleAtFixedRate()。

    【讨论】:

    • Date 有明确的schedule 方法
    • Well ExecutorService 是最新的,比 Timer 更好
    【解决方案5】:

    我建议您从 java 程序中删除调度逻辑。
    通过这样做,您可以只专注于您希望程序执行的操作,并将调度部分留给操作系统。
    另外,例如,假设您决定在某个时候编写一个 c++ 程序来执行您的 java 代码所知道的操作,您不必在新程序中实现 cron 逻辑

    话虽如此:

    • 对于 Linux,您有 crontab
    • 对于 Windows,您有 windows task schedule
    • 对于 Mac,我不确定,但考虑到它是基于 UNIX 的 cron 应该存在。

    【讨论】:

      【解决方案6】:

      将你的代码放入无限的while中并使用

      Thread.sleep(3600000);
      

      20点后开始执行

      示例

      while(1==1) {
          //Your code here
          try{
               Thread.sleep(3600000);
          }
          catch (Exception e) {}
      }
      

      【讨论】:

      • 这并不保证它会在每小时的第 20 分钟运行
      • 我试过这个,但我发现 Thread.sleep 并不总是准确的。
      【解决方案7】:

      为您希望每小时执行的方法安排一个 cron 作业,而不是阻止 sleep() 调用,使用一些调度框架,如 quartz

      【讨论】:

      • 为什么要让 cron 作业每年运行一次?
      【解决方案8】:

      你应该看看ScheduledExecutorService

      class BeeperControl {
      private final ScheduledExecutorService scheduler = 
         Executors.newScheduledThreadPool(1);
      
      public void beepForAnHour() {
          final Runnable beeper = new Runnable() {
                  public void run() { System.out.println("beep"); }
              };
          final ScheduledFuture<?> beeperHandle = 
              scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
          scheduler.schedule(new Runnable() {
                  public void run() { beeperHandle.cancel(true); }
              }, 60 * 60, SECONDS);
      }
      }
      

      【讨论】:

      • 主要挑战是计算初始延迟,使其发生在 20 分钟标记处。
      • @MarkoTopolnik 你明白了。他可以将其第一次安排为空运行,其中不会发生任何事情,然后开始处理。
      【解决方案9】:

      使用类似的东西

      Calendar calendar = Calendar.getInstance();
      calendar.set(Calendar.HOUR_OF_DAY, 20);
      calendar.set(Calendar.MINUTE, 20);
      calendar.set(Calendar.SECOND, 0);
      Date time = calendar.getTime();
      Timer timer = new Timer();
      timer.schedule(new SomeTask(), time);
      

      然后重新安排

      【讨论】:

        【解决方案10】:

        因此,正如许多其他答案所述,ScheduledExecutorService 绝对很棒。

        如果您使用的是 Java EE 6 服务器,您可以通过 @ScheduleScheduleExpression 获得一些乐趣

        Have an EJB schedule tasks with "crontab syntax"

        【讨论】:

          【解决方案11】:

          1) 在第一次输入时计算下一个到期时间。

          2) 使用java.util.Timer.schedule()

          3) 重新安排每次运行。

          代码

          package tests;
          
          import java.util.Calendar;
          import java.util.Date;
          import java.util.Timer;
          import java.util.TimerTask;
          
          public class App201210130040 {
          
          
          private static Timer timer = new Timer(false);
          
          public static void schedule() {
              Calendar due = Calendar.getInstance();
              due.set(Calendar.MINUTE, 20);
              if( due.before(Calendar.getInstance()) ) {
                  due.add(Calendar.HOUR, 1);
              }
          
              System.out.println("Scheduled to " + due.getTime().toString());
          
              timer.schedule(new TimerTask() {
          
                  @Override
                  public void run() {
          
                      System.out.println("due");
          
                      schedule();
                  }
          
              }, due.getTime());
          
          }
          
          
          public static void main(String[] args) {
              schedule();
          }
          
          }
          

          【讨论】:

            【解决方案12】:

            另一个例子

            import java.text.SimpleDateFormat;
            import java.util.Date;
            
            public class TestHour {
            
                private static final int MINUNTE = 20;
            
                public static void main(String args[]) {
                    while (true) {
                        SimpleDateFormat bartDateFormat = new SimpleDateFormat("mm");
                        Date date = new Date();
                        int currentMin = new Integer(bartDateFormat.format(date))
                                .intValue();
                        if (currentMin < MINUNTE) {
                            sleepMinutes(MINUNTE - currentMin);
                        } else if (currentMin > MINUNTE) {
                            sleepMinutes(60 - currentMin + MINUNTE);
                        } else {
                            // DO SOMETHING EVERY HOUR
                            System.out.println("come on do it!!!");
                            sleepMinutes(60);
                        }
                    }
                }
            
                private static void sleepMinutes(int minutes) {
                    try {
                        System.out.println("Sleeping for " + minutes);
                        Thread.sleep(minutes * 1000*60);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            

            【讨论】:

              【解决方案13】:

              今天学到的东西又是一个例子。

              import java.util.Calendar;
              import java.util.Date;
              import java.util.concurrent.Executors;
              import java.util.concurrent.ScheduledExecutorService;
              import java.util.concurrent.TimeUnit;
              
              public class SchedulerExample2 implements Runnable{
              
                  public static void main(String args[]) {
              
                      Calendar due = Calendar.getInstance();
                      due.set(Calendar.MILLISECOND, 0);
                      due.set(Calendar.SECOND, 0);
                      due.set(Calendar.MINUTE, 20);
                      if (due.before(Calendar.getInstance())) {
                          due.add(Calendar.HOUR, 1);
                      }
                      long milliSecondsToNextOcurrence =  due.getTimeInMillis() - new Date().getTime();
                      final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
                      ShedulerExample task = new  ShedulerExample();
                      s.scheduleAtFixedRate(task, milliSecondsToNextOcurrence, 60*60*1000, TimeUnit.MILLISECONDS);
                  }
              
                  @Override
                  public void run() {
                          System.out.println("hola->"+new Date());
                  }   
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-08-28
                • 1970-01-01
                • 2015-11-20
                • 2012-09-02
                • 1970-01-01
                • 1970-01-01
                • 2018-12-19
                相关资源
                最近更新 更多