如何使用计时器停止 Java 方法的执行?

Posted

技术标签:

【中文标题】如何使用计时器停止 Java 方法的执行?【英文标题】:How do you stop a java method execution with a timer? 【发布时间】:2022-01-21 16:44:18 【问题描述】:

我试图在执行 10 秒后停止长时间运行的方法,到目前为止,我遵循了 baeldung 上的计时器说明。

https://www.baeldung.com/java-stop-execution-after-certain-time#1-using-a-timer

当方法是对线程睡眠的简单调用时,它可以工作,但是当我使用子方法调用我的函数时,它不会停止。

我的实现如下所示:

 class TimeOutTask extends TimerTask 
        private Thread t;
        private Timer timer;

        TimeOutTask(Thread t, Timer timer)
            this.t = t;
            this.timer = timer;
        

        public void run() 
            if (t != null && t.isAlive()) 
                t.interrupt();
                timer.cancel();
            
        
    
    class Execution implements Runnable 

        private String carpeta;
        private Experiment exp;

        public Execution(String carpeta, Experiment exp) 
            this.carpeta = carpeta;
            this.exp = exp;
        

        @Override
        public void run() 
            try 
                while (!Thread.currentThread().isInterrupted()) 
                   exp.executeExperiment(carpeta);
                
             catch (InterruptedException e) 
                System.out.println("Fin de ejecución por tiempo");
            
        
    

我调用这个执行的方式是通过 executeTimedExperiment 方法

    public Experiment() 
        this.cases = new ArrayList<>();
    


    private void executeTimedExperiment(String carpeta)
        Thread t = new Thread(new Execution(carpeta,this));
        Timer timer = new Timer();
        timer.schedule(new TimeOutTask(t, timer), 10000);
        t.start();
    

    private void executeExperiment(String carpeta) throws InterruptedException 

        String[] files = getFiles(carpeta);
        Arrays.sort(files);

        for (String file : files) 
             executeCase(carpeta, file);
        

    

    private boolean executeCase(String carpeta, String file) 

        Graph g = readDataToGraph(carpeta + "/" + file);
        Solution s = new ExactSolutionGenerator().ExactSolution(g);
        addNewCase(file, s);

    

executeExperiment 方法是长时间运行的,我用 InterruptedException 对其进行了标记,但编译器告诉我永远不会抛出异常。

当我执行它时会发生什么,它可以正常运行而不会停止。

我不确定是否需要将 InterruptedException 添加到子方法或其他内容中,但如果可能的话,我希望不要触及子方法。

提前致谢。

【问题讨论】:

【参考方案1】:

编译器告诉你这个异常永远不会抛出是因为你的executeExperiment 方法是不可中断的(不像一些阻塞方法,例如Object#wait),所以thread.interrupt 不会让执行这个方法的线程收到InterruptedException。

也许你需要在你的executeExperiment方法中每次迭代files时检查当前线程是否已经中断,如果是,则抛出一个InterruptedException。(但这可能仍然不准确,因为executeCase方法可能会执行很长时间。)

【讨论】:

executeCase 是整个执行过程中最长的方法,我应该检查线程是否被插入到他的方法中? 是的。进一步缩小检查范围。 (作为VGR给出的答案)【参考方案2】:

您需要做的不仅仅是将throws InterruptedException 添加到所有这些“子方法”(以及您自己的方法)。必须更改每个方法的主体以正确响应中断。

不能任意停止运行代码。中断是合作的——它们只有在被中断的线程注意到它们时才有意义。

您的 run() 方法正确地做到了这一点:通过将整个循环放在 try/catch 中,任何 InterruptedException 都会导致循环终止,因此线程将终止。

但是它调用的方法必须做同样的事情。您的run 方法调用executeExperiment,它执行以下操作:

String[] files = getFiles(carpeta);

我不知道该方法需要多长时间,但如果它需要任何大量时间(超过几分之一秒),它需要能够在文件读取过程中抛出 InterruptedException .

executeExperiment 还调用 executeCase,后者调用 readDataToGraph、ExactSolution 和 addNewCase 的“子方法”。如上所述,每一个需要超过几分之一秒的方法都需要通过 throw InterruptedException 来响应中断。所以,恐怕你需要修改它们。

一个例子是:

private Graph readDataToGraph(String filename)
throws InterruptedException 

    Graph graph = new Graph();
    try (BufferedReader reader = Files.newBufferedReader(Path.of(filename))) 
        String line;
        while ((line = reader.readLine()) != null) 
            graph.addData(convertDataToGraphEntry(line));

            if (Thread.interrupted()) 
                throw new InterruptedException();
            
        
     catch (IOException e) 
        throw new UncheckedIOException(e);
    


【讨论】:

以上是关于如何使用计时器停止 Java 方法的执行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ElapsedEvent 期间保持 System.Timers.Timer 停止

如何停止在 C# 类中运行的计时器

js定时器如何在执行完成以后往下执行

如何在 Java 中计时方法的执行?

java中TimerTask 如何才能停止呢

我将如何更改我的程序,以便在方法正确后计时器停止