Java - 每隔一段时间调用一个函数

6
我有一个程序,可以将文件路径和对应的参数插入到表中。之后,我有另一个名为do_Scan()的函数,它会扫描该表并进行一些处理和索引。
但是,我希望这个函数do_Scan()在特定时间间隔内运行,比如每N分钟就调用此函数。这个N肯定是可配置的。
我考虑使用一个计时器类,但不太确定如何实现配置。我的想法是创建一个计时器函数,它将调用do_Scan()方法。
这个类应该像这样:
public void schedule(TimerTask task,long delay,long period){

}

我的主要方法:

public static void main(String[] args) throws Exception {

    Indexing test= new Indexing();
    java.sql.Timestamp date = new java.sql.Timestamp(new java.util.Date().getTime());
    // Exception e=e.printStackTrace();
    Scanner scanner = new Scanner(System.in);
    System.out.print("Enter a file path: ");
    System.out.flush();
    String filename = scanner.nextLine();
    File file = new File(filename);
    if(file.exists() && !file.isDirectory()) {
        test.index_request(filename,"Active",date,date,"");
    }else{
        test.index_request(filename,"Error",date,date,"Some errorCode");
    }

    // Call schedule() function 
}}

如何设置 Timer 类使其在一定时间间隔内无限运行?


例子?但我希望计时器在它自己的函数中。 - Daredevil
看看这个链接是否能帮到你:https://dev59.com/uWXWa4cB1Zd3GeqPRvHn - Amit Kumar Lal
是的,它确实有帮助。这是最好的完成方式吗? - Daredevil
建议使用“Runnable”的答案相当老了。改用lambda表达式(自Java 1.8起)。 - ETO
5个回答

4

最简单的方法是使用标准库中的一个类。

java.util.Timer

这里是一个使用它的简单示例:
import java.util.Timer; 
import java.util.TimerTask; 

class MyTask extends TimerTask 
{ 
   public static int i = 0; 
   public void run() 
   { 
      System.out.println("Hello, I'm timer, running iteration: " + ++i); 
   } 
} 

public class Test 
{ 
   public static void main(String[] args) 
   { 

      Timer timer = new Timer(); 
      TimerTask task = new MyTask(); 

      timer.schedule(task, 2000, 5000);  // 2000 - delay (can set to 0 for immediate execution), 5000 is a frequency.

   } 
} 

如果出现任何错误,那么我需要编写另一个函数来处理它。 - Daredevil
是的,当然可以,但这取决于您的自定义逻辑以及应用程序。定时器只能定期运行某些内容,它并不能帮助您处理错误。 - Mark Bramnik

3
您可以使用 ScheduledThreadPoolExecutor 来实现此目标:
假设您有一个任务方法:
public void task(String foo, Integer bar){
    // ...
}

Java 1.8之前

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
executor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        task(fooParam, barParam);  
    }
}, 0, 60, TimeUnit.SECONDS);

Java 1.8+

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.scheduleAtFixedRate(() -> task(fooParam, barParam), 0, 60, TimeUnit.SECONDS);

1
你为什么在Java 1.8之前的示例中选择2作为池大小? - amsjntz

0
为此,我建议您使用Java的ScheduledExecutorService,它为您提供了一个API来按固定速率安排任务(以及其他API)。
您有两个选项可供选择:
  1. 在你的任务中实现RunnableCallable(或类似的接口),并调用schedule()方法:

    public class IndexingTask implements Runnable {
    
        @Override
        public void run() {
            schedule();
        }
    
        private void schedule() {
            //做一些事情
        }
    }
    
    
    
    public static void main(String[] args) {
        //做一些事情
        long delay = getDelay();
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
        scheduledThreadPool.scheduleAtFixedRate(new IndexingTask(), 0, delay, TimeUnit.SECONDS);
    }
    
  2. 使用Lambda表达式来内联执行:

    public static void main(String[] args) {
        //做一些事情
        long delay = getDelay();
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
        scheduledThreadPool.scheduleAtFixedRate(() -> doSomething(), 0, delay, TimeUnit.SECONDS);
    }
    

如果您正在使用Spring Framework,您可以简单地使用以下代码注释:@Scheduled

@Scheduled(fixedRate = 5000)
public void schedule() {
    //do something
}

0

有很多方法来解决这个问题。我认为最简单的方法是,当你只在给定的地方使用这种方法而不使用诸如Spring之类的外部框架时:

private void runWithInterval(long millis) {
    Runnable task = () -> {
        try {
            while (true) {
                Thread.sleep(millis);
                // payload
            }
        } catch(InterruptedException e) {
        }
    };

    Thread thread = new Thread(task);
    thread.setDaemon(true);
    thread.start();
}

以1分钟间隔调用它:

runWithInterval(TimeUnit.MINUTES.toMillis(1));

提示:

其他细节可以在这里的其他帖子中查看:

  • 使用Spring时的@Scheduler注释
  • 使用单线程线程池:ScheduledExecutorService scheduledThreadPool = Executors.newFixedThreadPool(1)
  • 使用Timernew Timer().schedule(new Runnable() {}, TimeUnit.MINUTES.toMillis(1))

这种方法会导致漂移,因为有效载荷会影响时间。 - akauppi
"Thread.sleep" 会占用线程,从而浪费宝贵的线程时间,什么也不做? - wayne

0

如果您熟悉ReactiveX,那么有一个名为RxJava的Java库可用于以下用途:

Flowable.interval(1, TimeUnit.SECONDS) // Or any other desired interval
        .subscribe(iteration -> System.out.println("Hello, I'm timer, running iteration: " + iteration));

但你真的应该更多地了解这种方法和库


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接