在Java中,是否可以执行一个方法一段时间并在达到时间限制后停止?

3

我有这段代码可以下载网页:

HttpURLConnection connection;

private String downloadContent() {
    InputStream content;
    Source parser;
    try {
        content = connection.getInputStream(); //<--here is the download
        parser = new Source(content);            
        content.close();
        return parser.toString();
    } catch (Exception e) {
        return null;
    }
}

在下载过程中,我试图获取已下载数据的数量,如果达到一定限制,则停止下载,但我找不到方法来实现这一点。如果有人知道如何做,请告诉我。
现在我想限制下载时间。例如:如果下载超过20秒,我将停止它。我想这样做是因为我的程序是一个网络爬虫,如果由于错误开始下载一个大文件,它将卡在下载中,这不是我想要的结果,因此通过大小过滤下载是可以的,但由于我不知道如何过滤,所以过滤时间将能够解决这个问题。

通常这是通过设置超时时间来完成的。connection对象是什么? - mre
1
我本来想回答这个问题,但已经有人回答了。答案涉及创建一个线程来读取数据,然后在超时后要么中断它,要么关闭连接返回的流。 - Ravi Wallau
你正在使用哪个库?它应该为你提供一个超时方法。 - Andrew White
连接是HttpURLConnection。目前我有多种这些方法同时运行,每个方法在一个线程中。 - Renato Dinhani
5个回答

2

实现此目标的正确方式如下:

public class TimeOut {

    public static class MyJob implements Callable<String> {

        @Override
        public String call() throws Exception {
            // Do something
            return "result";
        }

    }

    public static void main(String[] args) {

        Future<String> control
                = Executors.newSingleThreadExecutor().submit(new MyJob());

        try {

            String result = control.get(5, TimeUnit.SECONDS);

        } catch (TimeoutException ex) {

            // 5 seconds expired, we cancel the job !!!
            control.cancel(true);

        }
        catch (InterruptedException ex) {

        } catch (ExecutionException ex) {

        }

    }

}

我现在感觉好蠢。我在我的程序中有这段完全一样的代码用于其他事情,但我从未意识到我可以使用它来做我想要的事情。我会测试。 - Renato Dinhani
3
问题在于后台任务通常不会被取消,它将继续运行。任务及其使用的任何方法都必须是可中断的。特别是在I/O的情况下,您必须使用正确配置的“InterruptibleChannel”。 - erickson

2
你可以使用AOP和jcabi-aspects中的@Timeable注解(我是开发者):
@Timeable(limit = 1, unit = TimeUnit.SECONDS)
String downloadContent() {
  if (Thread.currentThread.isInterrupted()) {
    throw new IllegalStateException("time out");
  }
  // download
}

请注意,您应该定期检查isInterrupted()并在其设置为TRUE时抛出异常。这是在Java中终止线程的唯一方法。
此外,要获得更详细的解释,请查看此帖子:http://www.yegor256.com/2014/06/20/limit-method-execution-time.html

1

生活是混乱的。如果你想要打扫干净,就需要付出一些努力。

private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(20);
private String downloadContent() {
  connection.setConnectTimeout(TIMEOUT); /* Set connect timeout. */
  long start = System.nanoTime(); 
  final InputStream content;
  try {
    content = connection.getInputStream();
  } catch (IOException ex) { 
    return null;
  }
  /* Compute how much time we have left. */
  final long delay = TIMEOUT - 
    TimeUnit.NANOS.toMillis(System.nanoTime() - time); 
  if (delay < 1)
    return null;
  /* Start a thread that can close the stream asynchronously. */
  Thread killer = new Thread() {
    @Override
    public void run() {
      try {
        Thread.sleep(delay); /* Wait until time runs out or interrupted. */
      } catch (InterruptedException expected) { 
        Thread.currentThread().interrupt();
      }
      try {
        content.close();
      } catch (IOException ignore) {
        // Log this?
      }
    }
  };
  killer.start();
  try {
    String s = new Source(content).parser.toString();
    /* Task completed in time; clean up immediately. */
    killer.interrupt();
    return s;
  } catch (Exception e) {
    return null;
  }
}

1

有一个指定的类java.util.Timer,旨在执行您所需的任务。您可以参考API获取更多详细信息。


计时器允许您选择开始的时间,但无法满足OP的要求... - Jérôme Verstrynge
我不知道你所说的“OP的请求”是什么意思?但是何时开始取决于您,您可以在任何想要的起始点指定它。 - Vacker

0

你不能停止一个正在运行的线程。但是你可以:

1)创建一个新的线程并从该线程获取内容。如果该线程花费太长时间来回答,那么就继续进行并忽略其结果。这种方法的缺点是:后台线程仍将下载大文件。

2)使用另一个具有更多控制的HTTP连接API。我很久以前使用过“Jakarta Commons HttpClient”,非常满意它的超时能力。


雅加达提供了有关下载文件大小的信息吗? - Renato Dinhani

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