Java - 在特定的超时时间后继续循环迭代

3
有没有一种方法可以在一定的超时时间后退出('continue;')循环迭代?
我有一个循环,它会运行并从网络收集数据,然后使用这些数据进行计算。
数据在约1到2秒后变得过时,因此如果循环迭代时间超过1秒,则希望它“继续”到下一次迭代。
有时收集数据可能需要时间,但有时计算可能需要超过1秒钟,因此HTTP超时对我所需的内容不起作用。 此外,在进行计算时,我使用的线程被阻塞,因此无法检查System.currentTimeMillis();
是否有一种方法可以使用另一个线程来检查时间并强制原始for循环继续。
4个回答

4
使用AsyncTask来执行阻塞计算,并拥有属于主线程的Handler。在onPreExecute()中,您可以Handler.postDelayed()一个调用AsyncTask.cancel(true)Runnable。在onPostExecute()中,您可以取消上述Runnable,因为如果计算及时完成,则不需要它。工作完成。

3

假设无法更改计算代码以检查标志(例如boolean stopSystem.currentTimeMillis()),我根据此进行回答。如果是这样,那么这是一个可行的解决方案。

您需要在每次期望得到新结果时生成一个新的计算。该程序具有一些问题,例如从未保证计算完成,导致无限数量的线程。再次强调,这是基于您无法提前停止计算的假设。如果您有该选项,您可以在计算循环中设置标志以提前退出方法。

我不知道为什么无法正确获取代码样式,我是这个站点的新手,任何帮助都将不胜感激

您将维护已处理的结果堆栈。如果始终获取顶部结果,则在此时它将是您可能处理的最新结果。我在此创建堆栈的原因是,除了覆盖先前的结果之外,您还可能需要对先前的计算执行某些操作。

我的示例中的performCalculation主体仅重要于模拟您提到的环境。

您可以创建一个新线程,或使用现有线程连续处理抛出到results中的结果。

import java.util.Random;
import java.util.Stack;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

public class Main
{
    private static int CALCULATION_THRESHOLD = 2000;

    private static Stack<Object> results = new Stack<Object>();

    private static Object resultTrigger = new Object();

    public static void main(String[] args)
    {
        Timer calculationTimer = new Timer(true);
        calculationTimer.schedule(new TimerTask() {
            @Override
            public void run()
            {
                Thread calculationThread = new Thread() {
                    public void run() {
                        Object result = performCalculation();
                        results.push(result);
                        synchronized(resultTrigger) {
                            resultTrigger.notifyAll();
                        }       
                    }
                };
                calculationThread.start();
            }
        }, CALCULATION_THRESHOLD, CALCULATION_THRESHOLD);

        synchronized(resultTrigger) {
            if (results.isEmpty()) {
                // This is bad as it will never end if you don't
                // get a result, add a timeout here. 
                try { resultTrigger.wait(); }
                catch (InterruptedException ex) {}
            }
        }

        // Get the next result
        Object result = results.pop();

        System.out.println ("Latest result is : " + result);

        // Do something with the remaining results or throw 
        // them away
        results.clear();
    }

    private static AtomicInteger counter = new AtomicInteger();

    // This is the method we are assuming can't be
    // changed to check for a stop flag.
    public static Object performCalculation()
    {
        int calcID = counter.addAndGet(1);
        System.out.println ("Calculation " + calcID + " is running.");
        Random randomGenerator = new Random();
        int sleep = randomGenerator.nextInt(10000);
        // Ensure we sleep for at least 2 seconds
        try { Thread.sleep(sleep + 2000);   }
        catch (InterruptedException ex) {}
        return String.valueOf(counter.get());
    }
}

示例的输出结果:

第一次运行

正在执行计算1。 正在执行计算2。 正在执行计算3。 最新结果为:3

第二次运行

正在执行计算1。 正在执行计算2。 正在执行计算3。 正在执行计算4。 正在执行计算5。 最新结果为:5

第三次运行

正在执行计算1。 正在执行计算2。 最新结果为:2


1
由于我目前无法评论其他人的答案,AsychTask可能很好,但它依赖于能够在计算线程中检查isCancelled。我阅读这个问题以假设根据这个声明“而且,在进行计算时,我正在使用的线程被阻塞,因此我无法检查System.currentTimeMillis();” cancel(true)在AsyncTask上不会自动取消线程,除非用户检查标志isCancelled()。 - Andrew T Finnell
希望你能尽快获得足够的积分,这样你就可以进行评论、点赞、踩等操作了。如果你能给出一个漂亮而详细的第一次回答,我会为你点赞的。 :) - SyntaxT3rr0r
Finnell:话说建议使用AsyncTask的答案更好 :) - SyntaxT3rr0r
@SpoonBender 如果AsyncTask能够检查isCancelled标志,那将会更好。如果计算在AsyncTask内运行了15秒钟,当AsyncTask.cancel(true)被调用时,它将不会被取消。如果由于某种原因(我怀疑),AsyncTask.cancel尝试中止()线程,那是可怕的。如果它尝试中断线程,仍然不能保证停止线程。一个例子是在AsyncTask执行中运行while(true){}。没有办法停止它,除非做一些非常糟糕的事情,比如中止线程。 - Andrew T Finnell
谢谢Andrew,你的所有假设都是正确的。'计算'是在外部Jar中完成的,因此我无法检查是什么原因导致有时候计算速度慢,更不用说在计算进行到一半时停止它了。 - Matt

1

看起来最简单的解决方案是将System.currentTimeMillis()添加到计算本身中,如果检测到运行时间过长,则无结果退出。当线程被解除阻塞时,您需要检查是否有结果,如果没有则“继续”。 当然,您可以使用另一个线程,但那可能会过度杀伤性。


0
我建议使用一个提交给ExecutorService的Callable任务,并在超时后运行它。代码应该相当直观:
import java.util.concurrent.*;

class InterruptibleProcessing{
    public static void main(String[] args){
        ExecutorService es = Executors.newSingleThreadExecutor();

        //the loop
        for(int i=0; i<iterations; i++){
            //run your data gathering process in a separate thread
            Future<Result> futureResult = es.submit(new Callable<Result>(){
                public Result call(){
                    //do you work here and return the result
                    return gatherData();
                }
            });

            try{
                //wait for result with timeout
                Result result = futureResult.get(1, TimeUnit.SECONDS);
                //if we are here then we have the result in less than 1 second
                // do something and exit the loop
                break;
            }catch(TimeoutException timeout){
                //Didn't finish in time, cancel the task, and proceed to
                //next iteration. This will send an interrupt signal to 
                //your task thread.
                futureResult.cancel(true);
            }
        }
    }
}

为了使其完美运行,您的数据收集任务需要检查线程中断。这主要包括:
1)在可能长时间使用CPU的循环内检查线程中断(不包括I/O)。如果您有大型循环进行重型处理,请确保在相对较短的间隔内运行此代码。
if(Thread.isInterrupted()){
    throw new InterruptedException();
    //or maybe some other code to stop processing
}

2) 做I/O操作的地方,如从套接字或文件中读取/写入通常会检查中断并抛出某种异常。可能抛出的异常类型有InterruptedIOExceptionClosedByInterruptException等等。通常,在相应的阻塞方法的API中会指定所抛出的异常类型。在Java锁上阻塞的方法(如Queue.take()等)会抛出InterruptedException


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