【问题标题】:Why SocketTimeoutException makes my program freeze?为什么 SocketTimeoutException 使我的程序冻结?
【发布时间】:2015-04-29 16:57:17
【问题描述】:

我为我的程序提供了超过 600 个链接,这些链接存储在 ArrayList 中,以获取网页的标题,使用 JSoup(以及其他)。对于每个链接(使用 for 循环),我创建一个新线程(使用thread.start())并通过我的程序传递链接,我等待我的线程完成(使用thread.join)之前启动一个新线程(同时执行会导致一些问题,我这样做是为了防止线程意外结束以停止其他链接的执行)。

问题在于,有时 JSoup 会抛出一个 SocketTimeoutException(我应该捕获它),这会使我的程序冻结。我不知道为什么即使被try/catch包围,执行也会停止。

这是我的一段代码,也许可以帮助你理解:

// In the method actionPerformed() of my JPanel

for(final String link : links)
{
    Thread t = new Thread()
    {
        public void run()
        {
            Analyzer.process(link);
        }
    };
    t.start();
    try 
    {
        t.join();
    } 
    catch (InterruptedException e) 
    {
        e.printStackTrace();
    }
}

在我的过程中:

// method process() of my Analyzer class
try 
{
    Document doc = Jsoup.connect(lien).userAgent("Mozilla").timeout(5*10000).get(); 
    //                    ^ EXCEPTION THROWN HERE ! ^

    title = doc.title();
}
catch (Exception e) 
{
    e.printStackTrace();
    erreurs+="Erreur lors de la lecture du titre\n";
}

这很烦人,因为这个过程非常长,我让它运行了一夜,今天发现我的程序在第 54 个链接处卡住了。 ^^'提前谢谢你!

编辑 - 更新

SercanOzdemir 建议我使用 ExecutorService,而不是创建线程并制作 start()-join(),所以我尝试了:

ExecutorService ex = Executors.newSingleThreadExecutor();
for(final String link : links)
{
    System.err.println("-- "+i+" --");              //DEBUG
    ex.execute(new Runnable(){
            @Override
            public void run(){
                try
                {
                    Analyzer.process(link);
                }
                catch( Exception e )
                {
                    e.printStackTrace();
                }

            }
    });
    i++;                                            //DEBUG
    }
ex.shutdown();

但它只打印我的调试链接。知道为什么它不运行我的进程吗?

【问题讨论】:

  • 问。启动一个线程只是为了立即join() 它而不做任何其他事情到底有什么意义? A. 没有。
  • @EJP 如果我只使用一个线程在 600 个链接上执行我的进程,那么只有一个线程中断会停止整个进程(我已经遇到了这个问题),如果我不执行每个过程一个接一个,我还有其他问题(特别是一些文件处理)。我理解你的想法,但这是我发现用我的知识解决问题的唯一方法。 :)

标签: java multithreading freeze executorservice socket-timeout-exception


【解决方案1】:

我没有用 jsoup 尝试过,但它是一种创建线程来执行任务并监控其状态的简单方法。

    ExecutorService executorService = Executors.newCachedThreadPool();
    Future future = null;
    for(final String link : links)
    {
        future = executorService.submit(new Runnable(){

        @Override
        public void run(){
            try{
               Analyzer.process(link);
            }
            catch( Exception e ){
                e.printStackTrace();
            }

        }
    });

        while(future != null
                && !future.isDone()
                && !future.isCancelled())
        {
            try {
                Thread.sleep(2000); // Or do something else
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    executorService.shutdown();

您更新后的问题中的代码将不起作用,因为您正在创建服务并立即覆盖它而无需等待它完成。

ExecutorService ex = Executors.newSingleThreadExecutor();
for(final String link : links)
{
  System.err.println("-- "+i+" --");              //DEBUG
  ex.execute(new Runnable(){ //Here it is created
        @Override
        public void run(){
            try
            {
                Analyzer.process(link);
            }
            catch( Exception e )
            {
                e.printStackTrace();
            }

        }
});
i++;                                            //DEBUG
//a split second later this loop finishes and overwrites the service again
}
ex.shutdown();

【讨论】:

  • while 循环是一个相当丑陋的解决方案......只需调用 executerService.awaitTermination()
  • 确实很难看,但是 executerService.awaitTermination();只有当他在主线程上没有其他事情时才有用。如果它是一个基于 UI 的程序,他可能会更新状态栏等。
  • @Victor 它与whil循环配合得很好,也许使用awaitTermination()会更好,但是它有2个参数,所以方法需要:executorService.awaitTermination(long, TimeUnit),所以我应该传递什么更好我的情况下的参数?
  • executorService.awaitTermination() 不能替代 while 循环。 while 循环正在检查是否 Runnable();已完成。 executorService.awaitTermination() 告诉 ExecutorService 不再接受 Runnables 并等待正在运行的线程退出。
  • @GregKing,好的,但是在这种情况下,主线程无论如何都被置于睡眠状态。关于 await():例如 (1000, TimeUnit.Miliseconds)``, (5, TimeUnit .Minutes)`等
【解决方案2】:

为每个链接打开一个新线程是一个非常糟糕的选择。

如果你想要一个多线程程序,考虑使用ExecutionService

如果我们遇到您的问题,我宁愿完全按照下面的方式更改此程序,因为您在连接部分捕获了异常,这并不能保证您的异常已在该部分引发。

new Runnable(){

            @Override
            public void run(){
                try{
                    // your codes
                }
                catch( Exception e ){
                    e.printStackTrace();
                }

            }
        }.run();

【讨论】:

  • 不知道ExecutionService的存在。虽然我不知道它是如何工作的,但它似乎很有希望,因为我可以与我的 JPanel 交互,所以我宁愿尝试使用这个选项。所以我会在我的 for 循环 (ExecutorService ex = Executors.newSingleThreadExecutor();) 之外创建一个新的执行程序服务,然后在我的循环中我会写:ex.execute(new Runnable() { [...] }); 并在我的循环之后,以ex.shutdown() 结束,对吗?
  • 并添加 ex.waitForTermination()`,否则某些提交的任务可能无法运行。
  • 另外,如果您的工作人员实现 Callable 而不是 Runnable,您可能会轻松返回操作的结果。
  • 是的,如果你想让它在后台工作,那是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 2020-02-23
  • 2022-12-09
相关资源
最近更新 更多